From 83cb16727085b18191f45eb0ede6bf1f97d67a7a Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 14 Oct 2009 17:48:38 +0200 Subject: nvram: Drop the BKL from nvram_open() It's safe to remove the BKL from nvram_open(): there's no open() versus read() races: nvram_init() is very simple and race-free, it registers the device then puts it into /proc - there's no state init to race with. Cc: Wim Van Sebroeck Cc: Al Viro Cc: Frederic Weisbecker Cc: Thomas Gleixner LKML-Reference: <1255116426-7270-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- drivers/char/nvram.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 2100a8f7bd86..7cf4518c030f 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -329,14 +329,12 @@ static int nvram_ioctl(struct inode *inode, struct file *file, static int nvram_open(struct inode *inode, struct file *file) { - lock_kernel(); spin_lock(&nvram_state_lock); if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || (nvram_open_mode & NVRAM_EXCL) || ((file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE))) { spin_unlock(&nvram_state_lock); - unlock_kernel(); return -EBUSY; } @@ -347,7 +345,6 @@ static int nvram_open(struct inode *inode, struct file *file) nvram_open_cnt++; spin_unlock(&nvram_state_lock); - unlock_kernel(); return 0; } -- cgit v1.2.3 From bc85b25e5de17d714e8001cb3dc0feb66eac2750 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sat, 19 Dec 2009 19:45:43 +0800 Subject: hwrng: nomadik - Add hardware RNG driver The hardware random number generator by ST is used in both the Nomadik 8815 SoC and the U8500. It returns 16 bits every 400ns with automatic delay if a read is issued too early. It depends on PLAT_NOMADIK. Signed-off-by: Alessandro Rubini Acked-by: Andrea Gallo Acked-by: Matt Mackall Signed-off-by: Herbert Xu --- drivers/char/hw_random/Kconfig | 12 ++++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/nomadik-rng.c | 103 +++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 drivers/char/hw_random/nomadik-rng.c (limited to 'drivers/char') diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 87060266ef91..6ea1014697d1 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -186,3 +186,15 @@ config HW_RANDOM_MXC_RNGA module will be called mxc-rnga. If unsure, say Y. + +config HW_RANDOM_NOMADIK + tristate "ST-Ericsson Nomadik Random Number Generator support" + depends on HW_RANDOM && PLAT_NOMADIK + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on ST-Ericsson SoCs (8815 and 8500). + + To compile this driver as a module, choose M here: the + module will be called nomadik-rng. + + If unsure, say Y. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 5eeb1303f0d0..4273308aa1e3 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o +obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c new file mode 100644 index 000000000000..a8b4c4010144 --- /dev/null +++ b/drivers/char/hw_random/nomadik-rng.c @@ -0,0 +1,103 @@ +/* + * Nomadik RNG support + * Copyright 2009 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + void __iomem *base = (void __iomem *)rng->priv; + + /* + * The register is 32 bits and gives 16 random bits (low half). + * A subsequent read will delay the core for 400ns, so we just read + * once and accept the very unlikely very small delay, even if wait==0. + */ + *(u16 *)data = __raw_readl(base + 8) & 0xffff; + return 2; +} + +/* we have at most one RNG per machine, granted */ +static struct hwrng nmk_rng = { + .name = "nomadik", + .read = nmk_rng_read, +}; + +static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id) +{ + void __iomem *base; + int ret; + + ret = amba_request_regions(dev, dev->dev.init_name); + if (ret) + return ret; + ret = -ENOMEM; + base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!base) + goto out_release; + nmk_rng.priv = (unsigned long)base; + ret = hwrng_register(&nmk_rng); + if (ret) + goto out_unmap; + return 0; + +out_unmap: + iounmap(base); +out_release: + amba_release_regions(dev); + return ret; +} + +static int nmk_rng_remove(struct amba_device *dev) +{ + void __iomem *base = (void __iomem *)nmk_rng.priv; + hwrng_unregister(&nmk_rng); + iounmap(base); + amba_release_regions(dev); + return 0; +} + +static struct amba_id nmk_rng_ids[] = { + { + .id = 0x000805e1, + .mask = 0x000fffff, /* top bits are rev and cfg: accept all */ + }, + {0, 0}, +}; + +static struct amba_driver nmk_rng_driver = { + .drv = { + .owner = THIS_MODULE, + .name = "rng", + }, + .probe = nmk_rng_probe, + .remove = nmk_rng_remove, + .id_table = nmk_rng_ids, +}; + +static int __init nmk_rng_init(void) +{ + return amba_driver_register(&nmk_rng_driver); +} + +static void __devexit nmk_rng_exit(void) +{ + amba_driver_unregister(&nmk_rng_driver); +} + +module_init(nmk_rng_init); +module_exit(nmk_rng_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 48a719c238bcbb72d6da79de9c5b3b93ab472107 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 22 Jan 2010 16:01:04 +0100 Subject: intel-agp: Switch to wbinvd_on_all_cpus Simplify if-statement while at it. [ hpa: we need to #include ] Cc: Dave Jones Cc: David Airlie Signed-off-by: Borislav Petkov LKML-Reference: <1264172467-25155-3-git-send-email-bp@amd64.org> Signed-off-by: H. Peter Anvin --- drivers/char/agp/intel-agp.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 3999a5f25f38..8a713f1e9653 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "agp.h" /* @@ -815,12 +816,6 @@ static void intel_i830_setup_flush(void) intel_i830_fini_flush(); } -static void -do_wbinvd(void *null) -{ - wbinvd(); -} - /* The chipset_flush interface needs to get data that has already been * flushed out of the CPU all the way out to main memory, because the GPU * doesn't snoop those buffers. @@ -837,12 +832,10 @@ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) memset(pg, 0, 1024); - if (cpu_has_clflush) { + if (cpu_has_clflush) clflush_cache_range(pg, 1024); - } else { - if (on_each_cpu(do_wbinvd, NULL, 1) != 0) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); - } + else if (wbinvd_on_all_cpus() != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); } /* The intel i830 automatically initializes the agp aperture during POST. -- cgit v1.2.3 From b51130685817d8c1b56386f9957405b5be2cdfe0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 03:44:57 +0000 Subject: hvc_console: Make the ops pointer const. This is nicer for modern R/O protection. And noone needs it non-const, so constify the callers as well. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah To: Christian Borntraeger Cc: linuxppc-dev@ozlabs.org Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_beat.c | 2 +- drivers/char/hvc_console.c | 7 ++++--- drivers/char/hvc_console.h | 7 ++++--- drivers/char/hvc_iseries.c | 2 +- drivers/char/hvc_iucv.c | 2 +- drivers/char/hvc_rtas.c | 2 +- drivers/char/hvc_udbg.c | 2 +- drivers/char/hvc_vio.c | 2 +- drivers/char/hvc_xen.c | 2 +- 9 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 0afc8b82212e..6913fc33270c 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) return cnt; } -static struct hv_ops hvc_beat_get_put_ops = { +static const struct hv_ops hvc_beat_get_put_ops = { .get_chars = hvc_beat_get_chars, .put_chars = hvc_beat_put_chars, }; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 416d3423150d..d8dac5820f0e 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index) * console interfaces but can still be used as a tty device. This has to be * static because kmalloc will not work during early console init. */ -static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; +static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; @@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref) * vty adapters do NOT get an hvc_instantiate() callback since they * appear after early console init. */ -int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) +int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) { struct hvc_struct *hp; @@ -749,7 +749,8 @@ static const struct tty_operations hvc_ops = { }; struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size) + const struct hv_ops *ops, + int outbuf_size) { struct hvc_struct *hp; int i; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 10950ca706d8..52ddf4d3716c 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -55,7 +55,7 @@ struct hvc_struct { int outbuf_size; int n_outbuf; uint32_t vtermno; - struct hv_ops *ops; + const struct hv_ops *ops; int irq_requested; int data; struct winsize ws; @@ -76,11 +76,12 @@ struct hv_ops { }; /* Register a vterm and a slot index for use as a console (console_init) */ -extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); +extern int hvc_instantiate(uint32_t vtermno, int index, + const struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size); + const struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ extern int hvc_remove(struct hvc_struct *hp); diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index 936d05bf37fa..fd0242676a2a 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -197,7 +197,7 @@ done: return sent; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = get_chars, .put_chars = put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index fe62bd0e17b7..21681a81cc35 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev) /* HVC operations */ -static struct hv_ops hvc_iucv_ops = { +static const struct hv_ops hvc_iucv_ops = { .get_chars = hvc_iucv_get_chars, .put_chars = hvc_iucv_put_chars, .notifier_add = hvc_iucv_notifier_add, diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 88590d040046..61c4a61558d9 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_rtas_get_put_ops = { +static const struct hv_ops hvc_rtas_get_put_ops = { .get_chars = hvc_rtas_read_console, .put_chars = hvc_rtas_write_console, }; diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c index bd63ba878a56..b0957e61a7be 100644 --- a/drivers/char/hvc_udbg.c +++ b/drivers/char/hvc_udbg.c @@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_udbg_ops = { +static const struct hv_ops hvc_udbg_ops = { .get_chars = hvc_udbg_get, .put_chars = hvc_udbg_put, }; diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 10be343d6ae7..27370e99c66f 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) return got; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = filtered_get_chars, .put_chars = hvc_put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index b1a71638c772..60446f82a3fc 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len) return recv; } -static struct hv_ops hvc_ops = { +static const struct hv_ops hvc_ops = { .get_chars = read_console, .put_chars = write_console, .notifier_add = notifier_add_irq, -- cgit v1.2.3 From 119ea10947cc1402abbf9d6200815b0606536906 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 03:44:58 +0000 Subject: hvc_console: Remove __devinit annotation from hvc_alloc Virtio consoles can be hotplugged, so hvc_alloc gets called from multiple sites: from the initial probe() routine as well as later on from workqueue handlers which aren't __devinit code. So, drop the __devinit annotation for hvc_alloc. Signed-off-by: Amit Shah Cc: linuxppc-dev@ozlabs.org Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_console.c | 6 +++--- drivers/char/hvc_console.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index d8dac5820f0e..4c3b59be286a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -748,9 +748,9 @@ static const struct tty_operations hvc_ops = { .chars_in_buffer = hvc_chars_in_buffer, }; -struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, - int outbuf_size) +struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, + const struct hv_ops *ops, + int outbuf_size) { struct hvc_struct *hp; int i; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 52ddf4d3716c..54381eba4e4a 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -80,8 +80,8 @@ extern int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ -extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, int outbuf_size); +extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data, + const struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ extern int hvc_remove(struct hvc_struct *hp); -- cgit v1.2.3 From 71a157e8edca55198e808f8561dd49017a54ee34 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 1 Feb 2010 21:34:14 -0700 Subject: of: add 'of_' prefix to machine_is_compatible() machine is compatible is an OF-specific call. It should have the of_ prefix to protect the global namespace. Signed-off-by: Grant Likely Acked-by: Michal Simek --- arch/powerpc/kernel/pci_64.c | 2 +- arch/powerpc/platforms/85xx/xes_mpc85xx.c | 4 +-- arch/powerpc/platforms/cell/cbe_powerbutton.c | 2 +- arch/powerpc/platforms/cell/ras.c | 2 +- arch/powerpc/platforms/pasemi/cpufreq.c | 4 +-- arch/powerpc/platforms/powermac/cpufreq_32.c | 14 ++++----- arch/powerpc/platforms/powermac/cpufreq_64.c | 14 ++++----- arch/powerpc/platforms/powermac/feature.c | 2 +- arch/powerpc/platforms/powermac/smp.c | 12 ++++---- arch/powerpc/platforms/powermac/time.c | 8 ++--- arch/powerpc/platforms/powermac/udbg_scc.c | 6 ++-- arch/powerpc/sysdev/grackle.c | 4 +-- drivers/char/hvc_beat.c | 2 +- drivers/gpu/drm/radeon/radeon_combios.c | 44 +++++++++++++-------------- drivers/macintosh/adb.c | 4 +-- drivers/macintosh/therm_pm72.c | 8 ++--- drivers/macintosh/therm_windtunnel.c | 2 +- drivers/macintosh/via-pmu-backlight.c | 8 ++--- drivers/macintosh/via-pmu.c | 8 ++--- drivers/macintosh/windfarm_core.c | 6 ++-- drivers/macintosh/windfarm_cpufreq_clamp.c | 6 ++-- drivers/macintosh/windfarm_lm75_sensor.c | 6 ++-- drivers/macintosh/windfarm_max6690_sensor.c | 6 ++-- drivers/macintosh/windfarm_pm112.c | 2 +- drivers/macintosh/windfarm_pm121.c | 2 +- drivers/macintosh/windfarm_pm81.c | 4 +-- drivers/macintosh/windfarm_pm91.c | 2 +- drivers/macintosh/windfarm_smu_sensors.c | 6 ++-- drivers/net/mace.c | 2 +- drivers/of/base.c | 6 ++-- drivers/serial/pmac_zilog.c | 6 ++-- drivers/video/aty/aty128fb.c | 14 ++++----- drivers/video/aty/atyfb_base.c | 8 ++--- drivers/video/aty/radeon_backlight.c | 6 ++-- include/linux/of_fdt.h | 2 +- sound/ppc/awacs.c | 24 +++++++-------- sound/ppc/burgundy.c | 4 +-- sound/ppc/pmac.c | 18 +++++------ sound/soc/fsl/efika-audio-fabric.c | 2 +- sound/soc/fsl/pcm030-audio-fabric.c | 2 +- 40 files changed, 142 insertions(+), 142 deletions(-) (limited to 'drivers/char') diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index ccf56ac92de5..d43fc65749c1 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -224,7 +224,7 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus, * G5 machines... So when something asks for bus 0 io base * (bus 0 is HT root), we return the AGP one instead. */ - if (in_bus == 0 && machine_is_compatible("MacRISC4")) { + if (in_bus == 0 && of_machine_is_compatible("MacRISC4")) { struct device_node *agp; agp = of_find_compatible_node(NULL, NULL, "u3-agp"); diff --git a/arch/powerpc/platforms/85xx/xes_mpc85xx.c b/arch/powerpc/platforms/85xx/xes_mpc85xx.c index 1b426050a2f9..0125604d096e 100644 --- a/arch/powerpc/platforms/85xx/xes_mpc85xx.c +++ b/arch/powerpc/platforms/85xx/xes_mpc85xx.c @@ -80,8 +80,8 @@ static void xes_mpc85xx_configure_l2(void __iomem *l2_base) printk(KERN_INFO "xes_mpc85xx: Enabling L2 as cache\n"); ctl = MPC85xx_L2CTL_L2E | MPC85xx_L2CTL_L2I; - if (machine_is_compatible("MPC8540") || - machine_is_compatible("MPC8560")) + if (of_machine_is_compatible("MPC8540") || + of_machine_is_compatible("MPC8560")) /* * Assume L2 SRAM is used fully for cache, so set * L2BLKSZ (bits 4:5) to match L2SIZ (bits 2:3). diff --git a/arch/powerpc/platforms/cell/cbe_powerbutton.c b/arch/powerpc/platforms/cell/cbe_powerbutton.c index dcddaa5fcb66..f75a4daa4ca2 100644 --- a/arch/powerpc/platforms/cell/cbe_powerbutton.c +++ b/arch/powerpc/platforms/cell/cbe_powerbutton.c @@ -48,7 +48,7 @@ static int __init cbe_powerbutton_init(void) int ret = 0; struct input_dev *dev; - if (!machine_is_compatible("IBM,CBPLUS-1.0")) { + if (!of_machine_is_compatible("IBM,CBPLUS-1.0")) { printk(KERN_ERR "%s: Not a cell blade.\n", __func__); ret = -ENODEV; goto out; diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c index 5e0a191764fc..608fd2b584c9 100644 --- a/arch/powerpc/platforms/cell/ras.c +++ b/arch/powerpc/platforms/cell/ras.c @@ -255,7 +255,7 @@ static int __init cbe_sysreset_init(void) { struct cbe_pmd_regs __iomem *regs; - sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0"); + sysreset_hack = of_machine_is_compatible("IBM,CBPLUS-1.0"); if (!sysreset_hack) return 0; diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c index be2527a516ea..d35e0520abf0 100644 --- a/arch/powerpc/platforms/pasemi/cpufreq.c +++ b/arch/powerpc/platforms/pasemi/cpufreq.c @@ -304,8 +304,8 @@ static struct cpufreq_driver pas_cpufreq_driver = { static int __init pas_cpufreq_init(void) { - if (!machine_is_compatible("PA6T-1682M") && - !machine_is_compatible("pasemi,pwrficient")) + if (!of_machine_is_compatible("PA6T-1682M") && + !of_machine_is_compatible("pasemi,pwrficient")) return -ENODEV; return cpufreq_register_driver(&pas_cpufreq_driver); diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c index 08d94e4cedd3..d4f127d18141 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_32.c +++ b/arch/powerpc/platforms/powermac/cpufreq_32.c @@ -657,31 +657,31 @@ static int __init pmac_cpufreq_setup(void) cur_freq = (*value) / 1000; /* Check for 7447A based MacRISC3 */ - if (machine_is_compatible("MacRISC3") && + if (of_machine_is_compatible("MacRISC3") && of_get_property(cpunode, "dynamic-power-step", NULL) && PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { pmac_cpufreq_init_7447A(cpunode); /* Check for other MacRISC3 machines */ - } else if (machine_is_compatible("PowerBook3,4") || - machine_is_compatible("PowerBook3,5") || - machine_is_compatible("MacRISC3")) { + } else if (of_machine_is_compatible("PowerBook3,4") || + of_machine_is_compatible("PowerBook3,5") || + of_machine_is_compatible("MacRISC3")) { pmac_cpufreq_init_MacRISC3(cpunode); /* Else check for iBook2 500/600 */ - } else if (machine_is_compatible("PowerBook4,1")) { + } else if (of_machine_is_compatible("PowerBook4,1")) { hi_freq = cur_freq; low_freq = 400000; set_speed_proc = pmu_set_cpu_speed; is_pmu_based = 1; } /* Else check for TiPb 550 */ - else if (machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { + else if (of_machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { hi_freq = cur_freq; low_freq = 500000; set_speed_proc = pmu_set_cpu_speed; is_pmu_based = 1; } /* Else check for TiPb 400 & 500 */ - else if (machine_is_compatible("PowerBook3,2")) { + else if (of_machine_is_compatible("PowerBook3,2")) { /* We only know about the 400 MHz and the 500Mhz model * they both have 300 MHz as low frequency */ diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c index 708c75133377..3ed288e68ec4 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ b/arch/powerpc/platforms/powermac/cpufreq_64.c @@ -398,11 +398,11 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpus) int rc = -ENODEV; /* Check supported platforms */ - if (machine_is_compatible("PowerMac8,1") || - machine_is_compatible("PowerMac8,2") || - machine_is_compatible("PowerMac9,1")) + if (of_machine_is_compatible("PowerMac8,1") || + of_machine_is_compatible("PowerMac8,2") || + of_machine_is_compatible("PowerMac9,1")) use_volts_smu = 1; - else if (machine_is_compatible("PowerMac11,2")) + else if (of_machine_is_compatible("PowerMac11,2")) use_volts_vdnap = 1; else return -ENODEV; @@ -729,9 +729,9 @@ static int __init g5_cpufreq_init(void) return -ENODEV; } - if (machine_is_compatible("PowerMac7,2") || - machine_is_compatible("PowerMac7,3") || - machine_is_compatible("RackMac3,1")) + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) rc = g5_pm72_cpufreq_init(cpus); #ifdef CONFIG_PMAC_SMU else diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index fbc9bbd74dbd..33e815f4466c 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2426,7 +2426,7 @@ static int __init probe_motherboard(void) } } for(i=0; imode_info.connector_table = radeon_connector_table; if (rdev->mode_info.connector_table == CT_NONE) { #ifdef CONFIG_PPC_PMAC - if (machine_is_compatible("PowerBook3,3")) { + if (of_machine_is_compatible("PowerBook3,3")) { /* powerbook with VGA */ rdev->mode_info.connector_table = CT_POWERBOOK_VGA; - } else if (machine_is_compatible("PowerBook3,4") || - machine_is_compatible("PowerBook3,5")) { + } else if (of_machine_is_compatible("PowerBook3,4") || + of_machine_is_compatible("PowerBook3,5")) { /* powerbook with internal tmds */ rdev->mode_info.connector_table = CT_POWERBOOK_INTERNAL; - } else if (machine_is_compatible("PowerBook5,1") || - machine_is_compatible("PowerBook5,2") || - machine_is_compatible("PowerBook5,3") || - machine_is_compatible("PowerBook5,4") || - machine_is_compatible("PowerBook5,5")) { + } else if (of_machine_is_compatible("PowerBook5,1") || + of_machine_is_compatible("PowerBook5,2") || + of_machine_is_compatible("PowerBook5,3") || + of_machine_is_compatible("PowerBook5,4") || + of_machine_is_compatible("PowerBook5,5")) { /* powerbook with external single link tmds (sil164) */ rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; - } else if (machine_is_compatible("PowerBook5,6")) { + } else if (of_machine_is_compatible("PowerBook5,6")) { /* powerbook with external dual or single link tmds */ rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; - } else if (machine_is_compatible("PowerBook5,7") || - machine_is_compatible("PowerBook5,8") || - machine_is_compatible("PowerBook5,9")) { + } else if (of_machine_is_compatible("PowerBook5,7") || + of_machine_is_compatible("PowerBook5,8") || + of_machine_is_compatible("PowerBook5,9")) { /* PowerBook6,2 ? */ /* powerbook with external dual link tmds (sil1178?) */ rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; - } else if (machine_is_compatible("PowerBook4,1") || - machine_is_compatible("PowerBook4,2") || - machine_is_compatible("PowerBook4,3") || - machine_is_compatible("PowerBook6,3") || - machine_is_compatible("PowerBook6,5") || - machine_is_compatible("PowerBook6,7")) { + } else if (of_machine_is_compatible("PowerBook4,1") || + of_machine_is_compatible("PowerBook4,2") || + of_machine_is_compatible("PowerBook4,3") || + of_machine_is_compatible("PowerBook6,3") || + of_machine_is_compatible("PowerBook6,5") || + of_machine_is_compatible("PowerBook6,7")) { /* ibook */ rdev->mode_info.connector_table = CT_IBOOK; - } else if (machine_is_compatible("PowerMac4,4")) { + } else if (of_machine_is_compatible("PowerMac4,4")) { /* emac */ rdev->mode_info.connector_table = CT_EMAC; - } else if (machine_is_compatible("PowerMac10,1")) { + } else if (of_machine_is_compatible("PowerMac10,1")) { /* mini with internal tmds */ rdev->mode_info.connector_table = CT_MINI_INTERNAL; - } else if (machine_is_compatible("PowerMac10,2")) { + } else if (of_machine_is_compatible("PowerMac10,2")) { /* mini with external tmds */ rdev->mode_info.connector_table = CT_MINI_EXTERNAL; - } else if (machine_is_compatible("PowerMac12,1")) { + } else if (of_machine_is_compatible("PowerMac12,1")) { /* PowerMac8,1 ? */ /* imac g5 isight */ rdev->mode_info.connector_table = CT_IMAC_G5_ISIGHT; diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 23741cec45e3..d840a109f833 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -322,8 +322,8 @@ static int __init adb_init(void) adb_controller = NULL; } else { #ifdef CONFIG_PPC - if (machine_is_compatible("AAPL,PowerBook1998") || - machine_is_compatible("PowerBook1,1")) + if (of_machine_is_compatible("AAPL,PowerBook1998") || + of_machine_is_compatible("PowerBook1,1")) sleepy_trackpad = 1; #endif /* CONFIG_PPC */ diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 454bc501df3c..5738d8bf2d97 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -1899,7 +1899,7 @@ static int create_control_loops(void) */ if (rackmac) cpu_pid_type = CPU_PID_TYPE_RACKMAC; - else if (machine_is_compatible("PowerMac7,3") + else if (of_machine_is_compatible("PowerMac7,3") && (cpu_count > 1) && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) { @@ -2234,10 +2234,10 @@ static int __init therm_pm72_init(void) { struct device_node *np; - rackmac = machine_is_compatible("RackMac3,1"); + rackmac = of_machine_is_compatible("RackMac3,1"); - if (!machine_is_compatible("PowerMac7,2") && - !machine_is_compatible("PowerMac7,3") && + if (!of_machine_is_compatible("PowerMac7,2") && + !of_machine_is_compatible("PowerMac7,3") && !rackmac) return -ENODEV; diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index ba48fd76396e..7fb8b4da35a7 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -490,7 +490,7 @@ g4fan_init( void ) info = of_get_property(np, "thermal-info", NULL); of_node_put(np); - if( !info || !machine_is_compatible("PowerMac3,6") ) + if( !info || !of_machine_is_compatible("PowerMac3,6") ) return -ENODEV; if( info->id != 3 ) { diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c index a348bb0791d3..4f3c4479c16a 100644 --- a/drivers/macintosh/via-pmu-backlight.c +++ b/drivers/macintosh/via-pmu-backlight.c @@ -150,13 +150,13 @@ void __init pmu_backlight_init() /* Special case for the old PowerBook since I can't test on it */ autosave = - machine_is_compatible("AAPL,3400/2400") || - machine_is_compatible("AAPL,3500"); + of_machine_is_compatible("AAPL,3400/2400") || + of_machine_is_compatible("AAPL,3500"); if (!autosave && !pmac_has_backlight_type("pmu") && - !machine_is_compatible("AAPL,PowerBook1998") && - !machine_is_compatible("PowerBook1,1")) + !of_machine_is_compatible("AAPL,PowerBook1998") && + !of_machine_is_compatible("PowerBook1,1")) return; snprintf(name, sizeof(name), "pmubl"); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index db379c381432..42764849eb78 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -463,8 +463,8 @@ static int __init via_pmu_dev_init(void) #endif #ifdef CONFIG_PPC32 - if (machine_is_compatible("AAPL,3400/2400") || - machine_is_compatible("AAPL,3500")) { + if (of_machine_is_compatible("AAPL,3400/2400") || + of_machine_is_compatible("AAPL,3500")) { int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_MODEL, 0); pmu_battery_count = 1; @@ -472,8 +472,8 @@ static int __init via_pmu_dev_init(void) pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET; else pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER; - } else if (machine_is_compatible("AAPL,PowerBook1998") || - machine_is_compatible("PowerBook1,1")) { + } else if (of_machine_is_compatible("AAPL,PowerBook1998") || + of_machine_is_compatible("PowerBook1,1")) { pmu_battery_count = 2; pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 075b4d99e354..437f55c5d18d 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -468,9 +468,9 @@ static int __init windfarm_core_init(void) DBG("wf: core loaded\n"); /* Don't register on old machines that use therm_pm72 for now */ - if (machine_is_compatible("PowerMac7,2") || - machine_is_compatible("PowerMac7,3") || - machine_is_compatible("RackMac3,1")) + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) return -ENODEV; platform_device_register(&wf_platform_device); return 0; diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c index 900aade06198..1a77a7c97d0e 100644 --- a/drivers/macintosh/windfarm_cpufreq_clamp.c +++ b/drivers/macintosh/windfarm_cpufreq_clamp.c @@ -76,9 +76,9 @@ static int __init wf_cpufreq_clamp_init(void) struct wf_control *clamp; /* Don't register on old machines that use therm_pm72 for now */ - if (machine_is_compatible("PowerMac7,2") || - machine_is_compatible("PowerMac7,3") || - machine_is_compatible("RackMac3,1")) + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) return -ENODEV; clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index ed6426a10773..d8257d35afde 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -239,9 +239,9 @@ static struct i2c_driver wf_lm75_driver = { static int __init wf_lm75_sensor_init(void) { /* Don't register on old machines that use therm_pm72 for now */ - if (machine_is_compatible("PowerMac7,2") || - machine_is_compatible("PowerMac7,3") || - machine_is_compatible("RackMac3,1")) + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) return -ENODEV; return i2c_add_driver(&wf_lm75_driver); } diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index a67b349319e9..b486eb929fde 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -188,9 +188,9 @@ static struct i2c_driver wf_max6690_driver = { static int __init wf_max6690_sensor_init(void) { /* Don't register on old machines that use therm_pm72 for now */ - if (machine_is_compatible("PowerMac7,2") || - machine_is_compatible("PowerMac7,3") || - machine_is_compatible("RackMac3,1")) + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) return -ENODEV; return i2c_add_driver(&wf_max6690_driver); } diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c index 73d695dc9e50..e0ee80700cde 100644 --- a/drivers/macintosh/windfarm_pm112.c +++ b/drivers/macintosh/windfarm_pm112.c @@ -676,7 +676,7 @@ static int __init wf_pm112_init(void) { struct device_node *cpu; - if (!machine_is_compatible("PowerMac11,2")) + if (!of_machine_is_compatible("PowerMac11,2")) return -ENODEV; /* Count the number of CPU cores */ diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c index 66ec4fb115bb..947d4afa25ca 100644 --- a/drivers/macintosh/windfarm_pm121.c +++ b/drivers/macintosh/windfarm_pm121.c @@ -1008,7 +1008,7 @@ static int __init pm121_init(void) { int rc = -ENODEV; - if (machine_is_compatible("PowerMac12,1")) + if (of_machine_is_compatible("PowerMac12,1")) rc = pm121_init_pm(); if (rc == 0) { diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index abbe206474f5..565d5b2adc95 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -779,8 +779,8 @@ static int __init wf_smu_init(void) { int rc = -ENODEV; - if (machine_is_compatible("PowerMac8,1") || - machine_is_compatible("PowerMac8,2")) + if (of_machine_is_compatible("PowerMac8,1") || + of_machine_is_compatible("PowerMac8,2")) rc = wf_init_pm(); if (rc == 0) { diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 764c525b2117..bea99168ff35 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c @@ -711,7 +711,7 @@ static int __init wf_smu_init(void) { int rc = -ENODEV; - if (machine_is_compatible("PowerMac9,1")) + if (of_machine_is_compatible("PowerMac9,1")) rc = wf_init_pm(); if (rc == 0) { diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index 9c567b93f417..3c193504bb80 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c @@ -363,9 +363,9 @@ smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps) * I yet have to figure out what's up with 8,2 and will have to * adjust for later, unless we can 100% trust the SDB partition... */ - if ((machine_is_compatible("PowerMac8,1") || - machine_is_compatible("PowerMac8,2") || - machine_is_compatible("PowerMac9,1")) && + if ((of_machine_is_compatible("PowerMac8,1") || + of_machine_is_compatible("PowerMac8,2") || + of_machine_is_compatible("PowerMac9,1")) && cpuvcp_version >= 2) { pow->quadratic = 1; DBG("windfarm: CPU Power using quadratic transform\n"); diff --git a/drivers/net/mace.c b/drivers/net/mace.c index d9fbad386389..43aea91e3369 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -206,7 +206,7 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i mp->port_aaui = port_aaui; else { /* Apple Network Server uses the AAUI port */ - if (machine_is_compatible("AAPL,ShinerESB")) + if (of_machine_is_compatible("AAPL,ShinerESB")) mp->port_aaui = 1; else { #ifdef CONFIG_MACE_AAUI_PORT diff --git a/drivers/of/base.c b/drivers/of/base.c index 785e9cc1b207..6bc8740c21ad 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -219,13 +219,13 @@ int of_device_is_compatible(const struct device_node *device, EXPORT_SYMBOL(of_device_is_compatible); /** - * machine_is_compatible - Test root of device tree for a given compatible value + * of_machine_is_compatible - Test root of device tree for a given compatible value * @compat: compatible string to look for in root node's compatible property. * * Returns true if the root node has the given value in its * compatible property. */ -int machine_is_compatible(const char *compat) +int of_machine_is_compatible(const char *compat) { struct device_node *root; int rc = 0; @@ -237,7 +237,7 @@ int machine_is_compatible(const char *compat) } return rc; } -EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(of_machine_is_compatible); /** * of_device_is_available - check if a device is available for use diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 683e66f18e8c..3e2ae4807ae2 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -2031,9 +2031,9 @@ static int __init pmz_console_setup(struct console *co, char *options) /* * XServe's default to 57600 bps */ - if (machine_is_compatible("RackMac1,1") - || machine_is_compatible("RackMac1,2") - || machine_is_compatible("MacRISC4")) + if (of_machine_is_compatible("RackMac1,1") + || of_machine_is_compatible("RackMac1,2") + || of_machine_is_compatible("MacRISC4")) baud = 57600; /* diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index e4e4d433b007..9ee67d6da710 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1931,22 +1931,22 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i * PowerMac2,2 summer 2000 iMacs * PowerMac4,1 january 2001 iMacs "flower power" */ - if (machine_is_compatible("PowerMac2,1") || - machine_is_compatible("PowerMac2,2") || - machine_is_compatible("PowerMac4,1")) + if (of_machine_is_compatible("PowerMac2,1") || + of_machine_is_compatible("PowerMac2,2") || + of_machine_is_compatible("PowerMac4,1")) default_vmode = VMODE_1024_768_75; /* iBook SE */ - if (machine_is_compatible("PowerBook2,2")) + if (of_machine_is_compatible("PowerBook2,2")) default_vmode = VMODE_800_600_60; /* PowerBook Firewire (Pismo), iBook Dual USB */ - if (machine_is_compatible("PowerBook3,1") || - machine_is_compatible("PowerBook4,1")) + if (of_machine_is_compatible("PowerBook3,1") || + of_machine_is_compatible("PowerBook4,1")) default_vmode = VMODE_1024_768_60; /* PowerBook Titanium */ - if (machine_is_compatible("PowerBook3,2")) + if (of_machine_is_compatible("PowerBook3,2")) default_vmode = VMODE_1152_768_60; if (default_cmode > 16) diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 5f1b5807a48f..e45ab8db2ddc 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2439,7 +2439,7 @@ static int __devinit aty_init(struct fb_info *info) * The Apple iBook1 uses non-standard memory frequencies. * We detect it and set the frequency manually. */ - if (machine_is_compatible("PowerBook2,1")) { + if (of_machine_is_compatible("PowerBook2,1")) { par->pll_limits.mclk = 70; par->pll_limits.xclk = 53; } @@ -2659,7 +2659,7 @@ static int __devinit aty_init(struct fb_info *info) FBINFO_HWACCEL_YPAN; #ifdef CONFIG_PMAC_BACKLIGHT - if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) { + if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) { /* * these bits let the 101 powerbook * wake up from sleep -- paulus @@ -2690,9 +2690,9 @@ static int __devinit aty_init(struct fb_info *info) if (M64_HAS(G3_PB_1024x768)) /* G3 PowerBook with 1024x768 LCD */ default_vmode = VMODE_1024_768_60; - else if (machine_is_compatible("iMac")) + else if (of_machine_is_compatible("iMac")) default_vmode = VMODE_1024_768_75; - else if (machine_is_compatible("PowerBook2,1")) + else if (of_machine_is_compatible("PowerBook2,1")) /* iBook with 800x600 LCD */ default_vmode = VMODE_800_600_60; else diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c index 1a056adb61c8..fa1198c4ccc5 100644 --- a/drivers/video/aty/radeon_backlight.c +++ b/drivers/video/aty/radeon_backlight.c @@ -175,9 +175,9 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo) #ifdef CONFIG_PMAC_BACKLIGHT pdata->negative = pdata->negative || - machine_is_compatible("PowerBook4,3") || - machine_is_compatible("PowerBook6,3") || - machine_is_compatible("PowerBook6,5"); + of_machine_is_compatible("PowerBook4,3") || + of_machine_is_compatible("PowerBook6,3") || + of_machine_is_compatible("PowerBook6,5"); #endif rinfo->info->bl_dev = bd; diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 8118d4559dd5..fbf29610616d 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -85,7 +85,7 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname, extern void finish_device_tree(void); extern void unflatten_device_tree(void); extern void early_init_devtree(void *); -extern int machine_is_compatible(const char *compat); +extern int of_machine_is_compatible(const char *compat); extern void print_properties(struct device_node *node); extern int prom_n_intr_cells(struct device_node* np); extern void prom_get_irq_senses(unsigned char *senses, int off, int max); diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 2e156467b814..b36679384b27 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -751,8 +751,8 @@ static void snd_pmac_awacs_suspend(struct snd_pmac *chip) static void snd_pmac_awacs_resume(struct snd_pmac *chip) { - if (machine_is_compatible("PowerBook3,1") - || machine_is_compatible("PowerBook3,2")) { + if (of_machine_is_compatible("PowerBook3,1") + || of_machine_is_compatible("PowerBook3,2")) { msleep(100); snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1] & ~MASK_PAROUT); @@ -780,16 +780,16 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip) } #endif /* CONFIG_PM */ -#define IS_PM7500 (machine_is_compatible("AAPL,7500") \ - || machine_is_compatible("AAPL,8500") \ - || machine_is_compatible("AAPL,9500")) -#define IS_PM5500 (machine_is_compatible("AAPL,e411")) -#define IS_BEIGE (machine_is_compatible("AAPL,Gossamer")) -#define IS_IMAC1 (machine_is_compatible("PowerMac2,1")) -#define IS_IMAC2 (machine_is_compatible("PowerMac2,2") \ - || machine_is_compatible("PowerMac4,1")) -#define IS_G4AGP (machine_is_compatible("PowerMac3,1")) -#define IS_LOMBARD (machine_is_compatible("PowerBook1,1")) +#define IS_PM7500 (of_machine_is_compatible("AAPL,7500") \ + || of_machine_is_compatible("AAPL,8500") \ + || of_machine_is_compatible("AAPL,9500")) +#define IS_PM5500 (of_machine_is_compatible("AAPL,e411")) +#define IS_BEIGE (of_machine_is_compatible("AAPL,Gossamer")) +#define IS_IMAC1 (of_machine_is_compatible("PowerMac2,1")) +#define IS_IMAC2 (of_machine_is_compatible("PowerMac2,2") \ + || of_machine_is_compatible("PowerMac4,1")) +#define IS_G4AGP (of_machine_is_compatible("PowerMac3,1")) +#define IS_LOMBARD (of_machine_is_compatible("PowerBook1,1")) static int imac1, imac2; diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 0accfe49735b..1f72e1c786bf 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -582,7 +582,7 @@ static int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip) static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify) { if (chip->auto_mute) { - int imac = machine_is_compatible("iMac"); + int imac = of_machine_is_compatible("iMac"); int reg, oreg; reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); @@ -620,7 +620,7 @@ static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_noti */ int __devinit snd_pmac_burgundy_init(struct snd_pmac *chip) { - int imac = machine_is_compatible("iMac"); + int imac = of_machine_is_compatible("iMac"); int i, err; /* Checks to see the chip is alive and kicking */ diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 7bc492ee77ec..85081172403f 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -922,11 +922,11 @@ static void __devinit detect_byte_swap(struct snd_pmac *chip) } /* it seems the Pismo & iBook can't byte-swap in hardware. */ - if (machine_is_compatible("PowerBook3,1") || - machine_is_compatible("PowerBook2,1")) + if (of_machine_is_compatible("PowerBook3,1") || + of_machine_is_compatible("PowerBook2,1")) chip->can_byte_swap = 0 ; - if (machine_is_compatible("PowerBook2,1")) + if (of_machine_is_compatible("PowerBook2,1")) chip->can_duplex = 0; } @@ -959,11 +959,11 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip) chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ /* check machine type */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) + if (of_machine_is_compatible("AAPL,3400/2400") + || of_machine_is_compatible("AAPL,3500")) chip->is_pbook_3400 = 1; - else if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) + else if (of_machine_is_compatible("PowerBook1,1") + || of_machine_is_compatible("AAPL,PowerBook1998")) chip->is_pbook_G3 = 1; chip->node = of_find_node_by_name(NULL, "awacs"); sound = of_node_get(chip->node); @@ -1033,8 +1033,8 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip) } if (of_device_is_compatible(sound, "tumbler")) { chip->model = PMAC_TUMBLER; - chip->can_capture = machine_is_compatible("PowerMac4,2") - || machine_is_compatible("PowerBook4,1"); + chip->can_capture = of_machine_is_compatible("PowerMac4,2") + || of_machine_is_compatible("PowerBook4,1"); chip->can_duplex = 0; // chip->can_byte_swap = 0; /* FIXME: check this */ chip->num_freqs = ARRAY_SIZE(tumbler_freqs); diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index 3326e2a1e863..1a5b8e0d6a34 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -55,7 +55,7 @@ static __init int efika_fabric_init(void) struct platform_device *pdev; int rc; - if (!machine_is_compatible("bplan,efika")) + if (!of_machine_is_compatible("bplan,efika")) return -ENODEV; card.platform = &mpc5200_audio_dma_platform; diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index b928ef7d28eb..6644cba7cbf2 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c @@ -55,7 +55,7 @@ static __init int pcm030_fabric_init(void) struct platform_device *pdev; int rc; - if (!machine_is_compatible("phytec,pcm030")) + if (!of_machine_is_compatible("phytec,pcm030")) return -ENODEV; card.platform = &mpc5200_audio_dma_platform; -- cgit v1.2.3 From c6be9c5ab426693a052e67d7469df19a1c50faf4 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 8 Jan 2010 11:30:13 -0700 Subject: cm4000_cs.c: Remove unnecessary cast The struct file 'private_data' member is a void *, the cast is not needed. Signed-off-by: H Hartley Sweeten Cc: Harald Welte Signed-off-by: Dominik Brodowski --- drivers/char/pcmcia/cm4000_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 2db4c0a29b05..c9bc896d68af 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1047,7 +1047,7 @@ release_io: static ssize_t cmm_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { - struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data; + struct cm4000_dev *dev = filp->private_data; unsigned int iobase = dev->p_dev->io.BasePort1; unsigned short s; unsigned char tmp; -- cgit v1.2.3 From a23ea92474e558b071d3e43d961ec767c31faebd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:14:55 +0530 Subject: virtio: console: comment cleanup Remove old lguest-style comments. [Amit: - wingify comments acc. to kernel style - indent comments ] Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 108 ++++++++++++++++++++--------------------- include/linux/virtio_console.h | 6 ++- 2 files changed, 58 insertions(+), 56 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a035ae39a359..26e238cd7d2f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1,18 +1,5 @@ -/*D:300 - * The Guest console driver - * - * Writing console drivers is one of the few remaining Dark Arts in Linux. - * Fortunately for us, the path of virtual consoles has been well-trodden by - * the PowerPC folks, who wrote "hvc_console.c" to generically support any - * virtual console. We use that infrastructure which only requires us to write - * the basic put_chars and get_chars functions and call the right register - * functions. - :*/ - -/*M:002 The console can be flooded: while the Guest is processing input the - * Host can send more. Buffering in the Host could alleviate this, but it is a - * difficult problem in general. :*/ -/* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation +/* + * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,8 +21,6 @@ #include #include "hvc_console.h" -/*D:340 These represent our input and output console queues, and the virtio - * operations for them. */ static struct virtqueue *in_vq, *out_vq; static struct virtio_device *vdev; @@ -49,12 +34,14 @@ static struct hv_ops virtio_cons; /* The hvc device */ static struct hvc_struct *hvc; -/*D:310 The put_chars() callback is pretty straightforward. +/* + * The put_chars() callback is pretty straightforward. * - * We turn the characters into a scatter-gather list, add it to the output - * queue and then kick the Host. Then we sit here waiting for it to finish: - * inefficient in theory, but in practice implementations will do it - * immediately (lguest's Launcher does). */ + * We turn the characters into a scatter-gather list, add it to the + * output queue and then kick the Host. Then we sit here waiting for + * it to finish: inefficient in theory, but in practice + * implementations will do it immediately (lguest's Launcher does). + */ static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; @@ -63,8 +50,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); - /* add_buf wants a token to identify this buffer: we hand it any - * non-NULL pointer, since there's only ever one buffer. */ + /* + * add_buf wants a token to identify this buffer: we hand it + * any non-NULL pointer, since there's only ever one buffer. + */ if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { /* Tell Host to go! */ out_vq->vq_ops->kick(out_vq); @@ -77,8 +66,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) return count; } -/* Create a scatter-gather list representing our input buffer and put it in the - * queue. */ +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + */ static void add_inbuf(void) { struct scatterlist sg[1]; @@ -90,12 +81,14 @@ static void add_inbuf(void) in_vq->vq_ops->kick(in_vq); } -/*D:350 get_chars() is the callback from the hvc_console infrastructure when - * an interrupt is received. +/* + * get_chars() is the callback from the hvc_console infrastructure + * when an interrupt is received. * - * Most of the code deals with the fact that the hvc_console() infrastructure - * only asks us for 16 bytes at a time. We keep in_offset and in_used fields - * for partially-filled buffers. */ + * Most of the code deals with the fact that the hvc_console() + * infrastructure only asks us for 16 bytes at a time. We keep + * in_offset and in_used fields for partially-filled buffers. + */ static int get_chars(u32 vtermno, char *buf, int count) { /* If we don't have an input queue yet, we can't get input. */ @@ -123,14 +116,16 @@ static int get_chars(u32 vtermno, char *buf, int count) return count; } -/*:*/ -/*D:320 Console drivers are initialized very early so boot messages can go out, - * so we do things slightly differently from the generic virtio initialization - * of the net and block drivers. +/* + * Console drivers are initialized very early so boot messages can go + * out, so we do things slightly differently from the generic virtio + * initialization of the net and block drivers. * - * At this stage, the console is output-only. It's too early to set up a - * virtqueue, so we let the drivers do some boutique early-output thing. */ + * At this stage, the console is output-only. It's too early to set + * up a virtqueue, so we let the drivers do some boutique early-output + * thing. + */ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) { virtio_cons.put_chars = put_chars; @@ -157,8 +152,8 @@ static void virtcons_apply_config(struct virtio_device *dev) } /* - * we support only one console, the hvc struct is a global var - * We set the configuration at this point, since we now have a tty + * we support only one console, the hvc struct is a global var We set + * the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { @@ -179,13 +174,17 @@ static void hvc_handle_input(struct virtqueue *vq) hvc_kick(); } -/*D:370 Once we're further in boot, we get probed like any other virtio device. - * At this stage we set up the output virtqueue. +/* + * Once we're further in boot, we get probed like any other virtio + * device. At this stage we set up the output virtqueue. * - * To set up and manage our virtual console, we call hvc_alloc(). Since we - * never remove the console device we never need this pointer again. + * To set up and manage our virtual console, we call hvc_alloc(). + * Since we never remove the console device we never need this pointer + * again. * - * Finally we put our input buffer in the input queue, ready to receive. */ + * Finally we put our input buffer in the input queue, ready to + * receive. + */ static int __devinit virtcons_probe(struct virtio_device *dev) { vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; @@ -203,8 +202,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev) } /* Find the queues. */ - /* FIXME: This is why we want to wean off hvc: we do nothing - * when input comes in. */ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); if (err) goto free; @@ -219,15 +216,18 @@ static int __devinit virtcons_probe(struct virtio_device *dev) virtio_cons.notifier_del = notifier_del_vio; virtio_cons.notifier_hangup = notifier_del_vio; - /* The first argument of hvc_alloc() is the virtual console number, so - * we use zero. The second argument is the parameter for the - * notification mechanism (like irq number). We currently leave this - * as zero, virtqueues have implicit notifications. + /* + * The first argument of hvc_alloc() is the virtual console + * number, so we use zero. The second argument is the + * parameter for the notification mechanism (like irq + * number). We currently leave this as zero, virtqueues have + * implicit notifications. * - * The third argument is a "struct hv_ops" containing the put_chars() - * get_chars(), notifier_add() and notifier_del() pointers. - * The final argument is the output buffer size: we can do any size, - * so we put PAGE_SIZE here. */ + * The third argument is a "struct hv_ops" containing the + * put_chars(), get_chars(), notifier_add() and notifier_del() + * pointers. The final argument is the output buffer size: we + * can do any size, so we put PAGE_SIZE here. + */ hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE); if (IS_ERR(hvc)) { err = PTR_ERR(hvc); diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index fe885174cc1f..9e0da40beae0 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -3,8 +3,10 @@ #include #include #include -/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers. */ +/* + * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. + */ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ -- cgit v1.2.3 From 971f3390003619ea4ac0b20ee93dfd3209025790 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:14:56 +0530 Subject: virtio: console: statically initialize virtio_cons That way, we can make it const as is good kernel style. We use a separate indirection for the early console, rather than mugging ops.put_chars. We rename it hv_ops, too. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 60 ++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 26 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 26e238cd7d2f..1d844a43a6bf 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -28,12 +28,12 @@ static struct virtio_device *vdev; static unsigned int in_len; static char *in, *inbuf; -/* The operations for our console. */ -static struct hv_ops virtio_cons; - /* The hvc device */ static struct hvc_struct *hvc; +/* This is the very early arch-specified put chars function. */ +static int (*early_put_chars)(u32, const char *, int); + /* * The put_chars() callback is pretty straightforward. * @@ -47,6 +47,9 @@ static int put_chars(u32 vtermno, const char *buf, int count) struct scatterlist sg[1]; unsigned int len; + if (unlikely(early_put_chars)) + return early_put_chars(vtermno, buf, count); + /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); @@ -117,21 +120,6 @@ static int get_chars(u32 vtermno, char *buf, int count) return count; } -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - virtio_cons.put_chars = put_chars; - return hvc_instantiate(0, 0, &virtio_cons); -} - /* * virtio console configuration. This supports: * - console resize @@ -174,6 +162,30 @@ static void hvc_handle_input(struct virtqueue *vq) hvc_kick(); } +/* The operations for the console. */ +static struct hv_ops hv_ops = { + .get_chars = get_chars, + .put_chars = put_chars, + .notifier_add = notifier_add_vio, + .notifier_del = notifier_del_vio, + .notifier_hangup = notifier_del_vio, +}; + +/* + * Console drivers are initialized very early so boot messages can go + * out, so we do things slightly differently from the generic virtio + * initialization of the net and block drivers. + * + * At this stage, the console is output-only. It's too early to set + * up a virtqueue, so we let the drivers do some boutique early-output + * thing. + */ +int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) +{ + early_put_chars = put_chars; + return hvc_instantiate(0, 0, &hv_ops); +} + /* * Once we're further in boot, we get probed like any other virtio * device. At this stage we set up the output virtqueue. @@ -209,13 +221,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev) in_vq = vqs[0]; out_vq = vqs[1]; - /* Start using the new console output. */ - virtio_cons.get_chars = get_chars; - virtio_cons.put_chars = put_chars; - virtio_cons.notifier_add = notifier_add_vio; - virtio_cons.notifier_del = notifier_del_vio; - virtio_cons.notifier_hangup = notifier_del_vio; - /* * The first argument of hvc_alloc() is the virtual console * number, so we use zero. The second argument is the @@ -228,7 +233,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE); + hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); if (IS_ERR(hvc)) { err = PTR_ERR(hvc); goto free_vqs; @@ -236,6 +241,9 @@ static int __devinit virtcons_probe(struct virtio_device *dev) /* Register the input buffer the first time. */ add_inbuf(); + + /* Start using the new console output. */ + early_put_chars = NULL; return 0; free_vqs: -- cgit v1.2.3 From 1dff399616a79b8ef5d61ad68f2ef1e1f590b465 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 28 Nov 2009 12:20:26 +0530 Subject: hvc_console: make the ops pointer const. This is nicer for modern R/O protection. And noone needs it non-const, so constify the callers as well. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah To: Christian Borntraeger Cc: linuxppc-dev@ozlabs.org --- drivers/char/hvc_beat.c | 2 +- drivers/char/hvc_console.c | 7 ++++--- drivers/char/hvc_console.h | 7 ++++--- drivers/char/hvc_iseries.c | 2 +- drivers/char/hvc_iucv.c | 2 +- drivers/char/hvc_rtas.c | 2 +- drivers/char/hvc_udbg.c | 2 +- drivers/char/hvc_vio.c | 2 +- drivers/char/hvc_xen.c | 2 +- drivers/char/virtio_console.c | 2 +- 10 files changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 0afc8b82212e..6913fc33270c 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) return cnt; } -static struct hv_ops hvc_beat_get_put_ops = { +static const struct hv_ops hvc_beat_get_put_ops = { .get_chars = hvc_beat_get_chars, .put_chars = hvc_beat_put_chars, }; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 416d3423150d..d8dac5820f0e 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index) * console interfaces but can still be used as a tty device. This has to be * static because kmalloc will not work during early console init. */ -static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; +static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; @@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref) * vty adapters do NOT get an hvc_instantiate() callback since they * appear after early console init. */ -int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) +int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) { struct hvc_struct *hp; @@ -749,7 +749,8 @@ static const struct tty_operations hvc_ops = { }; struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size) + const struct hv_ops *ops, + int outbuf_size) { struct hvc_struct *hp; int i; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 10950ca706d8..52ddf4d3716c 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -55,7 +55,7 @@ struct hvc_struct { int outbuf_size; int n_outbuf; uint32_t vtermno; - struct hv_ops *ops; + const struct hv_ops *ops; int irq_requested; int data; struct winsize ws; @@ -76,11 +76,12 @@ struct hv_ops { }; /* Register a vterm and a slot index for use as a console (console_init) */ -extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); +extern int hvc_instantiate(uint32_t vtermno, int index, + const struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, - struct hv_ops *ops, int outbuf_size); + const struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ extern int hvc_remove(struct hvc_struct *hp); diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index 936d05bf37fa..fd0242676a2a 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -197,7 +197,7 @@ done: return sent; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = get_chars, .put_chars = put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index fe62bd0e17b7..21681a81cc35 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev) /* HVC operations */ -static struct hv_ops hvc_iucv_ops = { +static const struct hv_ops hvc_iucv_ops = { .get_chars = hvc_iucv_get_chars, .put_chars = hvc_iucv_put_chars, .notifier_add = hvc_iucv_notifier_add, diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 88590d040046..61c4a61558d9 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_rtas_get_put_ops = { +static const struct hv_ops hvc_rtas_get_put_ops = { .get_chars = hvc_rtas_read_console, .put_chars = hvc_rtas_write_console, }; diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c index bd63ba878a56..b0957e61a7be 100644 --- a/drivers/char/hvc_udbg.c +++ b/drivers/char/hvc_udbg.c @@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) return i; } -static struct hv_ops hvc_udbg_ops = { +static const struct hv_ops hvc_udbg_ops = { .get_chars = hvc_udbg_get, .put_chars = hvc_udbg_put, }; diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 10be343d6ae7..27370e99c66f 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) return got; } -static struct hv_ops hvc_get_put_ops = { +static const struct hv_ops hvc_get_put_ops = { .get_chars = filtered_get_chars, .put_chars = hvc_put_chars, .notifier_add = notifier_add_irq, diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index b1a71638c772..60446f82a3fc 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len) return recv; } -static struct hv_ops hvc_ops = { +static const struct hv_ops hvc_ops = { .get_chars = read_console, .put_chars = write_console, .notifier_add = notifier_add_irq, diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 1d844a43a6bf..791be4e91d15 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -163,7 +163,7 @@ static void hvc_handle_input(struct virtqueue *vq) } /* The operations for the console. */ -static struct hv_ops hv_ops = { +static const struct hv_ops hv_ops = { .get_chars = get_chars, .put_chars = put_chars, .notifier_add = notifier_add_vio, -- cgit v1.2.3 From f550804ab92e37a08d2622522a0f11252a2158ea Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:14:59 +0530 Subject: virtio: console: We support only one device at a time We support only one virtio_console device at a time. If multiple are found, error out if one is already initialized. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 791be4e91d15..bfc0abf825ed 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -204,6 +204,11 @@ static int __devinit virtcons_probe(struct virtio_device *dev) struct virtqueue *vqs[2]; int err; + if (vdev) { + dev_warn(&vdev->dev, + "Multiple virtio-console devices not supported yet\n"); + return -EEXIST; + } vdev = dev; /* This is the scratch page we use to receive console input */ -- cgit v1.2.3 From 21206ede8826fd9d2eb72e05b429f3ccb1bdaff5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:15:00 +0530 Subject: virtio: console: port encapsulation We are heading towards a multiple-"port" system, so as part of weaning off globals we encapsulate the information into 'struct port'. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 107 +++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 49 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index bfc0abf825ed..9ea9223c5c5c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -21,15 +21,19 @@ #include #include "hvc_console.h" -static struct virtqueue *in_vq, *out_vq; -static struct virtio_device *vdev; - -/* This is our input buffer, and how much data is left in it. */ -static unsigned int in_len; -static char *in, *inbuf; +struct port { + struct virtqueue *in_vq, *out_vq; + struct virtio_device *vdev; + /* This is our input buffer, and how much data is left in it. */ + char *inbuf; + unsigned int used_len, offset; + + /* The hvc device */ + struct hvc_struct *hvc; +}; -/* The hvc device */ -static struct hvc_struct *hvc; +/* We have one port ready to go immediately, for a console. */ +static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); @@ -46,22 +50,21 @@ static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; unsigned int len; + struct port *port; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); + port = &console; + /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); - /* - * add_buf wants a token to identify this buffer: we hand it - * any non-NULL pointer, since there's only ever one buffer. - */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { + /* This shouldn't fail: if it does, we lose chars. */ + if (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) { /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); - /* Chill out until it's done with the buffer. */ - while (!out_vq->vq_ops->get_buf(out_vq, &len)) + port->out_vq->vq_ops->kick(port->out_vq); + while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len)) cpu_relax(); } @@ -73,15 +76,15 @@ static int put_chars(u32 vtermno, const char *buf, int count) * Create a scatter-gather list representing our input buffer and put * it in the queue. */ -static void add_inbuf(void) +static void add_inbuf(struct port *port) { struct scatterlist sg[1]; - sg_init_one(sg, inbuf, PAGE_SIZE); + sg_init_one(sg, port->inbuf, PAGE_SIZE); - /* We should always be able to add one buffer to an empty queue. */ - if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0) + /* Should always be able to add one buffer to an empty queue. */ + if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) BUG(); - in_vq->vq_ops->kick(in_vq); + port->in_vq->vq_ops->kick(port->in_vq); } /* @@ -94,28 +97,31 @@ static void add_inbuf(void) */ static int get_chars(u32 vtermno, char *buf, int count) { + struct port *port; + + port = &console; + /* If we don't have an input queue yet, we can't get input. */ - BUG_ON(!in_vq); + BUG_ON(!port->in_vq); - /* No buffer? Try to get one. */ - if (!in_len) { - in = in_vq->vq_ops->get_buf(in_vq, &in_len); - if (!in) + /* No more in buffer? See if they've (re)used it. */ + if (port->offset == port->used_len) { + if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len)) return 0; + port->offset = 0; } /* You want more than we have to give? Well, try wanting less! */ - if (in_len < count) - count = in_len; + if (port->offset + count > port->used_len) + count = port->used_len - port->offset; /* Copy across to their buffer and increment offset. */ - memcpy(buf, in, count); - in += count; - in_len -= count; + memcpy(buf, port->inbuf + port->offset, count); + port->offset += count; /* Finished? Re-register buffer so Host will use it again. */ - if (in_len == 0) - add_inbuf(); + if (port->offset == port->used_len) + add_inbuf(port); return count; } @@ -135,7 +141,7 @@ static void virtcons_apply_config(struct virtio_device *dev) dev->config->get(dev, offsetof(struct virtio_console_config, rows), &ws.ws_row, sizeof(u16)); - hvc_resize(hvc, ws); + hvc_resize(console.hvc, ws); } } @@ -146,7 +152,7 @@ static void virtcons_apply_config(struct virtio_device *dev) static int notifier_add_vio(struct hvc_struct *hp, int data) { hp->irq_requested = 1; - virtcons_apply_config(vdev); + virtcons_apply_config(console.vdev); return 0; } @@ -158,7 +164,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - if (hvc_poll(hvc)) + if (hvc_poll(console.hvc)) hvc_kick(); } @@ -197,23 +203,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) * Finally we put our input buffer in the input queue, ready to * receive. */ -static int __devinit virtcons_probe(struct virtio_device *dev) +static int __devinit virtcons_probe(struct virtio_device *vdev) { vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; const char *names[] = { "input", "output" }; struct virtqueue *vqs[2]; + struct port *port; int err; - if (vdev) { - dev_warn(&vdev->dev, + port = &console; + if (port->vdev) { + dev_warn(&port->vdev->dev, "Multiple virtio-console devices not supported yet\n"); return -EEXIST; } - vdev = dev; + port->vdev = vdev; /* This is the scratch page we use to receive console input */ - inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!inbuf) { + port->used_len = 0; + port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!port->inbuf) { err = -ENOMEM; goto fail; } @@ -223,8 +232,8 @@ static int __devinit virtcons_probe(struct virtio_device *dev) if (err) goto free; - in_vq = vqs[0]; - out_vq = vqs[1]; + port->in_vq = vqs[0]; + port->out_vq = vqs[1]; /* * The first argument of hvc_alloc() is the virtual console @@ -238,14 +247,14 @@ static int __devinit virtcons_probe(struct virtio_device *dev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(hvc)) { - err = PTR_ERR(hvc); + port->hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); + if (IS_ERR(port->hvc)) { + err = PTR_ERR(port->hvc); goto free_vqs; } /* Register the input buffer the first time. */ - add_inbuf(); + add_inbuf(port); /* Start using the new console output. */ early_put_chars = NULL; @@ -254,7 +263,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) free_vqs: vdev->config->del_vqs(vdev); free: - kfree(inbuf); + kfree(port->inbuf); fail: return err; } -- cgit v1.2.3 From fdb9a054554e1e435e927c9a47a999f026abd408 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:01 +0530 Subject: virtio: console: encapsulate buffer information in a struct Collect port buffer, used_len, offset fields into a single structure. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 70 +++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 15 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 9ea9223c5c5c..699fc98ec8d9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -21,12 +21,24 @@ #include #include "hvc_console.h" +struct port_buffer { + char *buf; + + /* size of the buffer in *buf above */ + size_t size; + + /* used length of the buffer */ + size_t len; + /* offset in the buf from which to consume data */ + size_t offset; +}; + struct port { struct virtqueue *in_vq, *out_vq; struct virtio_device *vdev; - /* This is our input buffer, and how much data is left in it. */ - char *inbuf; - unsigned int used_len, offset; + + /* The current buffer from which data has to be fed to readers */ + struct port_buffer *inbuf; /* The hvc device */ struct hvc_struct *hvc; @@ -38,6 +50,33 @@ static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); +static void free_buf(struct port_buffer *buf) +{ + kfree(buf->buf); + kfree(buf); +} + +static struct port_buffer *alloc_buf(size_t buf_size) +{ + struct port_buffer *buf; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + goto fail; + buf->buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf->buf) + goto free_buf; + buf->len = 0; + buf->offset = 0; + buf->size = buf_size; + return buf; + +free_buf: + kfree(buf); +fail: + return NULL; +} + /* * The put_chars() callback is pretty straightforward. * @@ -79,7 +118,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) static void add_inbuf(struct port *port) { struct scatterlist sg[1]; - sg_init_one(sg, port->inbuf, PAGE_SIZE); + sg_init_one(sg, port->inbuf->buf, PAGE_SIZE); /* Should always be able to add one buffer to an empty queue. */ if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) @@ -98,6 +137,7 @@ static void add_inbuf(struct port *port) static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; + unsigned int len; port = &console; @@ -105,22 +145,23 @@ static int get_chars(u32 vtermno, char *buf, int count) BUG_ON(!port->in_vq); /* No more in buffer? See if they've (re)used it. */ - if (port->offset == port->used_len) { - if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len)) + if (port->inbuf->offset == port->inbuf->len) { + if (!port->in_vq->vq_ops->get_buf(port->in_vq, &len)) return 0; - port->offset = 0; + port->inbuf->offset = 0; + port->inbuf->len = len; } /* You want more than we have to give? Well, try wanting less! */ - if (port->offset + count > port->used_len) - count = port->used_len - port->offset; + if (port->inbuf->offset + count > port->inbuf->len) + count = port->inbuf->len - port->inbuf->offset; /* Copy across to their buffer and increment offset. */ - memcpy(buf, port->inbuf + port->offset, count); - port->offset += count; + memcpy(buf, port->inbuf->buf + port->inbuf->offset, count); + port->inbuf->offset += count; /* Finished? Re-register buffer so Host will use it again. */ - if (port->offset == port->used_len) + if (port->inbuf->offset == port->inbuf->len) add_inbuf(port); return count; @@ -220,8 +261,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) port->vdev = vdev; /* This is the scratch page we use to receive console input */ - port->used_len = 0; - port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + port->inbuf = alloc_buf(PAGE_SIZE); if (!port->inbuf) { err = -ENOMEM; goto fail; @@ -263,7 +303,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) free_vqs: vdev->config->del_vqs(vdev); free: - kfree(port->inbuf); + free_buf(port->inbuf); fail: return err; } -- cgit v1.2.3 From e27b519807e04d950802cb89f7b22933d8d2f837 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:02 +0530 Subject: virtio: console: ensure add_inbuf can work for multiple ports as well add_inbuf() assumed one port and one inbuf per port. Remove that assumption. Also move the function so that put_chars and get_chars are together. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 699fc98ec8d9..1dbd46cb1a26 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -77,6 +77,22 @@ fail: return NULL; } +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + * + * Callers should take appropriate locks. + */ +static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) +{ + struct scatterlist sg[1]; + sg_init_one(sg, buf->buf, buf->size); + + if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0) + BUG(); + vq->vq_ops->kick(vq); +} + /* * The put_chars() callback is pretty straightforward. * @@ -111,21 +127,6 @@ static int put_chars(u32 vtermno, const char *buf, int count) return count; } -/* - * Create a scatter-gather list representing our input buffer and put - * it in the queue. - */ -static void add_inbuf(struct port *port) -{ - struct scatterlist sg[1]; - sg_init_one(sg, port->inbuf->buf, PAGE_SIZE); - - /* Should always be able to add one buffer to an empty queue. */ - if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0) - BUG(); - port->in_vq->vq_ops->kick(port->in_vq); -} - /* * get_chars() is the callback from the hvc_console infrastructure * when an interrupt is received. @@ -162,7 +163,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* Finished? Re-register buffer so Host will use it again. */ if (port->inbuf->offset == port->inbuf->len) - add_inbuf(port); + add_inbuf(port->in_vq, port->inbuf); return count; } @@ -294,7 +295,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) } /* Register the input buffer the first time. */ - add_inbuf(port); + add_inbuf(port->in_vq, port->inbuf); /* Start using the new console output. */ early_put_chars = NULL; -- cgit v1.2.3 From a3cde44908429e52b2ec052ad5a70ef60e1f2d56 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:03 +0530 Subject: virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq This makes taking locks around the get_buf vq operation easier, as well as complements the add_inbuf() operation. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 1dbd46cb1a26..df45e5e9a947 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -77,6 +77,22 @@ fail: return NULL; } +/* Callers should take appropriate locks */ +static void *get_inbuf(struct port *port) +{ + struct port_buffer *buf; + struct virtqueue *vq; + unsigned int len; + + vq = port->in_vq; + buf = vq->vq_ops->get_buf(vq, &len); + if (buf) { + buf->len = len; + buf->offset = 0; + } + return buf; +} + /* * Create a scatter-gather list representing our input buffer and put * it in the queue. @@ -138,7 +154,6 @@ static int put_chars(u32 vtermno, const char *buf, int count) static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; - unsigned int len; port = &console; @@ -147,10 +162,8 @@ static int get_chars(u32 vtermno, char *buf, int count) /* No more in buffer? See if they've (re)used it. */ if (port->inbuf->offset == port->inbuf->len) { - if (!port->in_vq->vq_ops->get_buf(port->in_vq, &len)) + if (!get_inbuf(port)) return 0; - port->inbuf->offset = 0; - port->inbuf->len = len; } /* You want more than we have to give? Well, try wanting less! */ -- cgit v1.2.3 From 73954488b1cc74cf46d6b94b8d3175f45496bd32 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:15:04 +0530 Subject: virtio: console: use vdev->priv to avoid accessing global var. Part of removing our "one console" assumptions, use vdev->priv to point to the port (currently == the global console). Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index df45e5e9a947..e52ee1151f5f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -187,6 +187,7 @@ static int get_chars(u32 vtermno, char *buf, int count) */ static void virtcons_apply_config(struct virtio_device *dev) { + struct port *port = dev->priv; struct winsize ws; if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) { @@ -196,7 +197,7 @@ static void virtcons_apply_config(struct virtio_device *dev) dev->config->get(dev, offsetof(struct virtio_console_config, rows), &ws.ws_row, sizeof(u16)); - hvc_resize(console.hvc, ws); + hvc_resize(port->hvc, ws); } } @@ -219,7 +220,9 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - if (hvc_poll(console.hvc)) + struct port *port = vq->vdev->priv; + + if (hvc_poll(port->hvc)) hvc_kick(); } @@ -272,7 +275,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) "Multiple virtio-console devices not supported yet\n"); return -EEXIST; } + + /* Attach this port to this virtio_device, and vice-versa. */ port->vdev = vdev; + vdev->priv = port; /* This is the scratch page we use to receive console input */ port->inbuf = alloc_buf(PAGE_SIZE); -- cgit v1.2.3 From 38edf58d73c28b082ec808aecdeb0ef2b92af049 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:05 +0530 Subject: virtio: console: don't assume a single console port. Keep a list of all ports being used as a console, and provide a lock and a lookup function. The hvc callbacks only give us a vterm number, so we need to map this. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 74 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e52ee1151f5f..6bbf707f9e33 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -17,10 +17,28 @@ */ #include #include +#include +#include #include #include #include "hvc_console.h" +/* + * This is a global struct for storing common data for all the devices + * this driver handles. + * + * Mainly, it has a linked list for all the consoles in one place so + * that callbacks from hvc for get_chars(), put_chars() work properly + * across multiple devices and multiple ports per device. + */ +struct ports_driver_data { + /* All the console devices handled by this driver */ + struct list_head consoles; +}; +static struct ports_driver_data pdrvdata; + +DEFINE_SPINLOCK(pdrvdata_lock); + struct port_buffer { char *buf; @@ -40,8 +58,15 @@ struct port { /* The current buffer from which data has to be fed to readers */ struct port_buffer *inbuf; + /* For console ports, hvc != NULL and these are valid. */ /* The hvc device */ struct hvc_struct *hvc; + + /* We'll place all consoles in a list in the pdrvdata struct */ + struct list_head list; + + /* Our vterm number. */ + u32 vtermno; }; /* We have one port ready to go immediately, for a console. */ @@ -50,6 +75,22 @@ static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); +static struct port *find_port_by_vtermno(u32 vtermno) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(port, &pdrvdata.consoles, list) { + if (port->vtermno == vtermno) + goto out; + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + static void free_buf(struct port_buffer *buf) { kfree(buf->buf); @@ -120,14 +161,16 @@ static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; - unsigned int len; struct port *port; + unsigned int len; + + port = find_port_by_vtermno(vtermno); + if (!port) + return 0; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); - port = &console; - /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); @@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; - port = &console; + + port = find_port_by_vtermno(vtermno); + if (!port) + return 0; /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); @@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev) } } -/* - * we support only one console, the hvc struct is a global var We set - * the configuration at this point, since we now have a tty - */ +/* We set the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { + struct port *port; + + port = find_port_by_vtermno(hp->vtermno); + if (!port) + return -EINVAL; + hp->irq_requested = 1; - virtcons_apply_config(console.vdev); + virtcons_apply_config(port->vdev); return 0; } @@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) goto free_vqs; } + /* Add to vtermno list. */ + spin_lock_irq(&pdrvdata_lock); + list_add(&port->list, &pdrvdata.consoles); + spin_unlock_irq(&pdrvdata_lock); + /* Register the input buffer the first time. */ add_inbuf(port->in_vq, port->inbuf); @@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = { static int __init init(void) { + INIT_LIST_HEAD(&pdrvdata.consoles); + return register_virtio_driver(&virtio_console); } module_init(init); -- cgit v1.2.3 From d8a02bd58ab6da4495a2d1af74d980c217e9abcf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jan 2010 19:15:06 +0530 Subject: virtio: console: remove global var Now we can use an allocation function to remove our global console variable. Signed-off-by: Rusty Russell Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 67 +++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 6bbf707f9e33..82f5180d4a37 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -32,6 +32,18 @@ * across multiple devices and multiple ports per device. */ struct ports_driver_data { + /* + * This is used to keep track of the number of hvc consoles + * spawned by this driver. This number is given as the first + * argument to hvc_alloc(). To correctly map an initial + * console spawned via hvc_instantiate to the console being + * hooked up via hvc_alloc, we need to pass the same vtermno. + * + * We also just assume the first console being initialised was + * the first one that got used as the initial console. + */ + unsigned int next_vtermno; + /* All the console devices handled by this driver */ struct list_head consoles; }; @@ -69,9 +81,6 @@ struct port { u32 vtermno; }; -/* We have one port ready to go immediately, for a console. */ -static struct port console; - /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); @@ -299,6 +308,30 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) return hvc_instantiate(0, 0, &hv_ops); } +static struct port *__devinit add_port(u32 vtermno) +{ + struct port *port; + + port = kmalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return NULL; + + port->inbuf = alloc_buf(PAGE_SIZE); + if (!port->inbuf) { + kfree(port); + return NULL; + } + port->hvc = NULL; + port->vtermno = vtermno; + return port; +} + +static void free_port(struct port *port) +{ + free_buf(port->inbuf); + kfree(port); +} + /* * Once we're further in boot, we get probed like any other virtio * device. At this stage we set up the output virtqueue. @@ -318,24 +351,16 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) struct port *port; int err; - port = &console; - if (port->vdev) { - dev_warn(&port->vdev->dev, - "Multiple virtio-console devices not supported yet\n"); - return -EEXIST; + port = add_port(pdrvdata.next_vtermno); + if (!port) { + err = -ENOMEM; + goto fail; } /* Attach this port to this virtio_device, and vice-versa. */ port->vdev = vdev; vdev->priv = port; - /* This is the scratch page we use to receive console input */ - port->inbuf = alloc_buf(PAGE_SIZE); - if (!port->inbuf) { - err = -ENOMEM; - goto fail; - } - /* Find the queues. */ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); if (err) @@ -346,17 +371,16 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) /* * The first argument of hvc_alloc() is the virtual console - * number, so we use zero. The second argument is the - * parameter for the notification mechanism (like irq - * number). We currently leave this as zero, virtqueues have - * implicit notifications. + * number. The second argument is the parameter for the + * notification mechanism (like irq number). We currently + * leave this as zero, virtqueues have implicit notifications. * * The third argument is a "struct hv_ops" containing the * put_chars(), get_chars(), notifier_add() and notifier_del() * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - port->hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE); + port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE); if (IS_ERR(port->hvc)) { err = PTR_ERR(port->hvc); goto free_vqs; @@ -364,6 +388,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) /* Add to vtermno list. */ spin_lock_irq(&pdrvdata_lock); + pdrvdata.next_vtermno++; list_add(&port->list, &pdrvdata.consoles); spin_unlock_irq(&pdrvdata_lock); @@ -377,7 +402,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) free_vqs: vdev->config->del_vqs(vdev); free: - free_buf(port->inbuf); + free_port(port); fail: return err; } -- cgit v1.2.3 From 1c85bf35449196e74deb487961d2f90c98f7b7ff Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:07 +0530 Subject: virtio: console: struct ports for multiple ports per device. Rather than assume a single port, add a 'struct ports_device' which stores data related to all the ports for that device. Currently, there's only one port and is hooked up with hvc, but that will change. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 152 +++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 63 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 82f5180d4a37..8631d431fe7f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -51,6 +51,15 @@ static struct ports_driver_data pdrvdata; DEFINE_SPINLOCK(pdrvdata_lock); +/* + * This is a per-device struct that stores data common to all the + * ports for that device (vdev->priv). + */ +struct ports_device { + struct virtqueue *in_vq, *out_vq; + struct virtio_device *vdev; +}; + struct port_buffer { char *buf; @@ -63,13 +72,17 @@ struct port_buffer { size_t offset; }; +/* This struct holds the per-port data */ struct port { - struct virtqueue *in_vq, *out_vq; - struct virtio_device *vdev; + /* Pointer to the parent virtio_console device */ + struct ports_device *portdev; /* The current buffer from which data has to be fed to readers */ struct port_buffer *inbuf; + /* The IO vqs for this port */ + struct virtqueue *in_vq, *out_vq; + /* For console ports, hvc != NULL and these are valid. */ /* The hvc device */ struct hvc_struct *hvc; @@ -152,6 +165,7 @@ static void *get_inbuf(struct port *port) static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) { struct scatterlist sg[1]; + sg_init_one(sg, buf->buf, buf->size); if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0) @@ -171,6 +185,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; struct port *port; + struct virtqueue *out_vq; unsigned int len; port = find_port_by_vtermno(vtermno); @@ -180,14 +195,15 @@ static int put_chars(u32 vtermno, const char *buf, int count) if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); + out_vq = port->out_vq; /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); /* This shouldn't fail: if it does, we lose chars. */ - if (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) { + if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, port) >= 0) { /* Tell Host to go! */ - port->out_vq->vq_ops->kick(port->out_vq); - while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len)) + out_vq->vq_ops->kick(out_vq); + while (!out_vq->vq_ops->get_buf(out_vq, &len)) cpu_relax(); } @@ -207,7 +223,6 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; - port = find_port_by_vtermno(vtermno); if (!port) return 0; @@ -242,7 +257,6 @@ static int get_chars(u32 vtermno, char *buf, int count) */ static void virtcons_apply_config(struct virtio_device *dev) { - struct port *port = dev->priv; struct winsize ws; if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) { @@ -252,7 +266,9 @@ static void virtcons_apply_config(struct virtio_device *dev) dev->config->get(dev, offsetof(struct virtio_console_config, rows), &ws.ws_row, sizeof(u16)); - hvc_resize(port->hvc, ws); + /* This is the pre-multiport style: we use control messages + * these days which specify the port. So this means port 0. */ + hvc_resize(find_port_by_vtermno(0)->hvc, ws); } } @@ -266,7 +282,7 @@ static int notifier_add_vio(struct hvc_struct *hp, int data) return -EINVAL; hp->irq_requested = 1; - virtcons_apply_config(port->vdev); + virtcons_apply_config(port->portdev->vdev); return 0; } @@ -278,9 +294,13 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - struct port *port = vq->vdev->priv; + struct port *port; + bool activity = false; + + list_for_each_entry(port, &pdrvdata.consoles, list) + activity |= hvc_poll(port->hvc); - if (hvc_poll(port->hvc)) + if (activity) hvc_kick(); } @@ -308,66 +328,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) return hvc_instantiate(0, 0, &hv_ops); } -static struct port *__devinit add_port(u32 vtermno) -{ - struct port *port; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) - return NULL; - - port->inbuf = alloc_buf(PAGE_SIZE); - if (!port->inbuf) { - kfree(port); - return NULL; - } - port->hvc = NULL; - port->vtermno = vtermno; - return port; -} - -static void free_port(struct port *port) +static int __devinit add_port(struct ports_device *portdev) { - free_buf(port->inbuf); - kfree(port); -} - -/* - * Once we're further in boot, we get probed like any other virtio - * device. At this stage we set up the output virtqueue. - * - * To set up and manage our virtual console, we call hvc_alloc(). - * Since we never remove the console device we never need this pointer - * again. - * - * Finally we put our input buffer in the input queue, ready to - * receive. - */ -static int __devinit virtcons_probe(struct virtio_device *vdev) -{ - vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; - const char *names[] = { "input", "output" }; - struct virtqueue *vqs[2]; struct port *port; int err; - port = add_port(pdrvdata.next_vtermno); + port = kmalloc(sizeof(*port), GFP_KERNEL); if (!port) { err = -ENOMEM; goto fail; } - /* Attach this port to this virtio_device, and vice-versa. */ - port->vdev = vdev; - vdev->priv = port; + port->portdev = portdev; + port->in_vq = portdev->in_vq; + port->out_vq = portdev->out_vq; - /* Find the queues. */ - err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); - if (err) - goto free; - - port->in_vq = vqs[0]; - port->out_vq = vqs[1]; + port->inbuf = alloc_buf(PAGE_SIZE); + if (!port->inbuf) { + err = -ENOMEM; + goto free_port; + } /* * The first argument of hvc_alloc() is the virtual console @@ -380,10 +360,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ + port->vtermno = pdrvdata.next_vtermno; port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE); if (IS_ERR(port->hvc)) { err = PTR_ERR(port->hvc); - goto free_vqs; + goto free_inbuf; } /* Add to vtermno list. */ @@ -395,6 +376,51 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) /* Register the input buffer the first time. */ add_inbuf(port->in_vq, port->inbuf); + return 0; + +free_inbuf: + free_buf(port->inbuf); +free_port: + kfree(port); +fail: + return err; +} + +/* + * Once we're further in boot, we get probed like any other virtio + * device. + */ +static int __devinit virtcons_probe(struct virtio_device *vdev) +{ + vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; + const char *names[] = { "input", "output" }; + struct virtqueue *vqs[2]; + struct ports_device *portdev; + int err; + + portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); + if (!portdev) { + err = -ENOMEM; + goto fail; + } + + /* Attach this portdev to this virtio_device, and vice-versa. */ + portdev->vdev = vdev; + vdev->priv = portdev; + + /* Find the queues. */ + err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); + if (err) + goto free; + + portdev->in_vq = vqs[0]; + portdev->out_vq = vqs[1]; + + /* We only have one port. */ + err = add_port(portdev); + if (err) + goto free_vqs; + /* Start using the new console output. */ early_put_chars = NULL; return 0; @@ -402,7 +428,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) free_vqs: vdev->config->del_vqs(vdev); free: - free_port(port); + kfree(portdev); fail: return err; } -- cgit v1.2.3 From cb06e3676b22013e9b759627e41656ddb07dee6d Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:08 +0530 Subject: virtio: console: ensure console size is updated on hvc open When multiple console support is added, ensure each port's size gets updated when a new one is opened via hvc. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 8631d431fe7f..debc86542858 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -251,27 +251,28 @@ static int get_chars(u32 vtermno, char *buf, int count) return count; } -/* - * virtio console configuration. This supports: - * - console resize - */ -static void virtcons_apply_config(struct virtio_device *dev) +static void resize_console(struct port *port) { + struct virtio_device *vdev; struct winsize ws; - if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) { - dev->config->get(dev, - offsetof(struct virtio_console_config, cols), - &ws.ws_col, sizeof(u16)); - dev->config->get(dev, - offsetof(struct virtio_console_config, rows), - &ws.ws_row, sizeof(u16)); - /* This is the pre-multiport style: we use control messages - * these days which specify the port. So this means port 0. */ - hvc_resize(find_port_by_vtermno(0)->hvc, ws); + vdev = port->portdev->vdev; + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { + vdev->config->get(vdev, + offsetof(struct virtio_console_config, cols), + &ws.ws_col, sizeof(u16)); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, rows), + &ws.ws_row, sizeof(u16)); + hvc_resize(port->hvc, ws); } } +static void virtcons_apply_config(struct virtio_device *vdev) +{ + resize_console(find_port_by_vtermno(0)); +} + /* We set the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { @@ -282,7 +283,7 @@ static int notifier_add_vio(struct hvc_struct *hp, int data) return -EINVAL; hp->irq_requested = 1; - virtcons_apply_config(port->portdev->vdev); + resize_console(port); return 0; } -- cgit v1.2.3 From 4f23c573c0dbebfd193cfb90b003d67929c58b31 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:09 +0530 Subject: virtio: console: Separate out console-specific data into a separate struct Move out console-specific stuff into a separate struct from 'struct port' as we need to maintain two lists: one for all the ports (which includes consoles) and one only for consoles since the hvc callbacks only give us the vtermno. This makes console handling cleaner. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 57 ++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index debc86542858..c6c6f52043b5 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -51,6 +51,24 @@ static struct ports_driver_data pdrvdata; DEFINE_SPINLOCK(pdrvdata_lock); +/* This struct holds information that's relevant only for console ports */ +struct console { + /* We'll place all consoles in a list in the pdrvdata struct */ + struct list_head list; + + /* The hvc device associated with this console port */ + struct hvc_struct *hvc; + + /* + * This number identifies the number that we used to register + * with hvc in hvc_instantiate() and hvc_alloc(); this is the + * number passed on by the hvc callbacks to us to + * differentiate between the other console ports handled by + * this driver + */ + u32 vtermno; +}; + /* * This is a per-device struct that stores data common to all the * ports for that device (vdev->priv). @@ -83,15 +101,11 @@ struct port { /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; - /* For console ports, hvc != NULL and these are valid. */ - /* The hvc device */ - struct hvc_struct *hvc; - - /* We'll place all consoles in a list in the pdrvdata struct */ - struct list_head list; - - /* Our vterm number. */ - u32 vtermno; + /* + * The entries in this struct will be valid if this port is + * hooked up to an hvc console + */ + struct console cons; }; /* This is the very early arch-specified put chars function. */ @@ -100,12 +114,15 @@ static int (*early_put_chars)(u32, const char *, int); static struct port *find_port_by_vtermno(u32 vtermno) { struct port *port; + struct console *cons; unsigned long flags; spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(port, &pdrvdata.consoles, list) { - if (port->vtermno == vtermno) + list_for_each_entry(cons, &pdrvdata.consoles, list) { + if (cons->vtermno == vtermno) { + port = container_of(cons, struct port, cons); goto out; + } } port = NULL; out: @@ -264,7 +281,7 @@ static void resize_console(struct port *port) vdev->config->get(vdev, offsetof(struct virtio_console_config, rows), &ws.ws_row, sizeof(u16)); - hvc_resize(port->hvc, ws); + hvc_resize(port->cons.hvc, ws); } } @@ -295,11 +312,11 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - struct port *port; + struct console *cons; bool activity = false; - list_for_each_entry(port, &pdrvdata.consoles, list) - activity |= hvc_poll(port->hvc); + list_for_each_entry(cons, &pdrvdata.consoles, list) + activity |= hvc_poll(cons->hvc); if (activity) hvc_kick(); @@ -361,17 +378,17 @@ static int __devinit add_port(struct ports_device *portdev) * pointers. The final argument is the output buffer size: we * can do any size, so we put PAGE_SIZE here. */ - port->vtermno = pdrvdata.next_vtermno; - port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(port->hvc)) { - err = PTR_ERR(port->hvc); + port->cons.vtermno = pdrvdata.next_vtermno; + port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); + if (IS_ERR(port->cons.hvc)) { + err = PTR_ERR(port->cons.hvc); goto free_inbuf; } /* Add to vtermno list. */ spin_lock_irq(&pdrvdata_lock); pdrvdata.next_vtermno++; - list_add(&port->list, &pdrvdata.consoles); + list_add(&port->cons.list, &pdrvdata.consoles); spin_unlock_irq(&pdrvdata_lock); /* Register the input buffer the first time. */ -- cgit v1.2.3 From cfa6d3792550c9eac51887181358954e6515da6b Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:10 +0530 Subject: virtio: console: Separate out console init into a new function Console ports could be hot-added. Also, with the new multiport support, a port is identified as a console port only if the host sends a control message. Move the console port init into a separate function so it can be invoked from other places. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 61 +++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 22 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c6c6f52043b5..11e5fafcca52 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -346,6 +346,43 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) return hvc_instantiate(0, 0, &hv_ops); } +int __devinit init_port_console(struct port *port) +{ + int ret; + + /* + * The Host's telling us this port is a console port. Hook it + * up with an hvc console. + * + * To set up and manage our virtual console, we call + * hvc_alloc(). + * + * The first argument of hvc_alloc() is the virtual console + * number. The second argument is the parameter for the + * notification mechanism (like irq number). We currently + * leave this as zero, virtqueues have implicit notifications. + * + * The third argument is a "struct hv_ops" containing the + * put_chars() get_chars(), notifier_add() and notifier_del() + * pointers. The final argument is the output buffer size: we + * can do any size, so we put PAGE_SIZE here. + */ + port->cons.vtermno = pdrvdata.next_vtermno; + + port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); + if (IS_ERR(port->cons.hvc)) { + ret = PTR_ERR(port->cons.hvc); + port->cons.hvc = NULL; + return ret; + } + spin_lock_irq(&pdrvdata_lock); + pdrvdata.next_vtermno++; + list_add_tail(&port->cons.list, &pdrvdata.consoles); + spin_unlock_irq(&pdrvdata_lock); + + return 0; +} + static int __devinit add_port(struct ports_device *portdev) { struct port *port; @@ -367,29 +404,9 @@ static int __devinit add_port(struct ports_device *portdev) goto free_port; } - /* - * The first argument of hvc_alloc() is the virtual console - * number. The second argument is the parameter for the - * notification mechanism (like irq number). We currently - * leave this as zero, virtqueues have implicit notifications. - * - * The third argument is a "struct hv_ops" containing the - * put_chars(), get_chars(), notifier_add() and notifier_del() - * pointers. The final argument is the output buffer size: we - * can do any size, so we put PAGE_SIZE here. - */ - port->cons.vtermno = pdrvdata.next_vtermno; - port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(port->cons.hvc)) { - err = PTR_ERR(port->cons.hvc); + err = init_port_console(port); + if (err) goto free_inbuf; - } - - /* Add to vtermno list. */ - spin_lock_irq(&pdrvdata_lock); - pdrvdata.next_vtermno++; - list_add(&port->cons.list, &pdrvdata.consoles); - spin_unlock_irq(&pdrvdata_lock); /* Register the input buffer the first time. */ add_inbuf(port->in_vq, port->inbuf); -- cgit v1.2.3 From 2658a79acf014deb0eaff2063f8f7a2b59f41285 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:11 +0530 Subject: virtio: console: Separate out find_vqs operation into a different function With support for multiple ports, each port will have its own input and output vqs. Prepare the probe function for this change. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 98 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 11e5fafcca52..75c5a3512ecd 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -74,7 +74,9 @@ struct console { * ports for that device (vdev->priv). */ struct ports_device { - struct virtqueue *in_vq, *out_vq; + /* Array of per-port IO virtqueues */ + struct virtqueue **in_vqs, **out_vqs; + struct virtio_device *vdev; }; @@ -395,8 +397,8 @@ static int __devinit add_port(struct ports_device *portdev) } port->portdev = portdev; - port->in_vq = portdev->in_vq; - port->out_vq = portdev->out_vq; + port->in_vq = portdev->in_vqs[0]; + port->out_vq = portdev->out_vqs[0]; port->inbuf = alloc_buf(PAGE_SIZE); if (!port->inbuf) { @@ -421,15 +423,87 @@ fail: return err; } +static int init_vqs(struct ports_device *portdev) +{ + vq_callback_t **io_callbacks; + char **io_names; + struct virtqueue **vqs; + u32 nr_ports, nr_queues; + int err; + + /* We currently only have one port and two queues for that port */ + nr_ports = 1; + nr_queues = 2; + + vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); + if (!vqs) { + err = -ENOMEM; + goto fail; + } + io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); + if (!io_callbacks) { + err = -ENOMEM; + goto free_vqs; + } + io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); + if (!io_names) { + err = -ENOMEM; + goto free_callbacks; + } + portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), + GFP_KERNEL); + if (!portdev->in_vqs) { + err = -ENOMEM; + goto free_names; + } + portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), + GFP_KERNEL); + if (!portdev->out_vqs) { + err = -ENOMEM; + goto free_invqs; + } + + io_callbacks[0] = hvc_handle_input; + io_callbacks[1] = NULL; + io_names[0] = "input"; + io_names[1] = "output"; + + /* Find the queues. */ + err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, + io_callbacks, + (const char **)io_names); + if (err) + goto free_outvqs; + + portdev->in_vqs[0] = vqs[0]; + portdev->out_vqs[0] = vqs[1]; + + kfree(io_callbacks); + kfree(io_names); + kfree(vqs); + + return 0; + +free_names: + kfree(io_names); +free_callbacks: + kfree(io_callbacks); +free_outvqs: + kfree(portdev->out_vqs); +free_invqs: + kfree(portdev->in_vqs); +free_vqs: + kfree(vqs); +fail: + return err; +} + /* * Once we're further in boot, we get probed like any other virtio * device. */ static int __devinit virtcons_probe(struct virtio_device *vdev) { - vq_callback_t *callbacks[] = { hvc_handle_input, NULL}; - const char *names[] = { "input", "output" }; - struct virtqueue *vqs[2]; struct ports_device *portdev; int err; @@ -443,13 +517,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) portdev->vdev = vdev; vdev->priv = portdev; - /* Find the queues. */ - err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); - if (err) + err = init_vqs(portdev); + if (err < 0) { + dev_err(&vdev->dev, "Error %d initializing vqs\n", err); goto free; - - portdev->in_vq = vqs[0]; - portdev->out_vq = vqs[1]; + } /* We only have one port. */ err = add_port(portdev); @@ -462,6 +534,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) free_vqs: vdev->config->del_vqs(vdev); + kfree(portdev->in_vqs); + kfree(portdev->out_vqs); free: kfree(portdev); fail: -- cgit v1.2.3 From 203baab8ba3195dd929473ba95b91c2b838833e6 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 19:15:12 +0530 Subject: virtio: console: Introduce function to hand off data from host to readers In preparation for serving data to userspace (generic ports) as well as in-kernel users (hvc consoles), separate out the functionality common to both in a 'fill_readbuf()' function. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 142 +++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 36 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 75c5a3512ecd..5096d92f5b89 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -100,6 +100,13 @@ struct port { /* The current buffer from which data has to be fed to readers */ struct port_buffer *inbuf; + /* + * To protect the operations on the in_vq associated with this + * port. Has to be a spinlock because it can be called from + * interrupt context (get_char()). + */ + spinlock_t inbuf_lock; + /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; @@ -132,6 +139,25 @@ out: return port; } +static struct port *find_port_by_vq(struct ports_device *portdev, + struct virtqueue *vq) +{ + struct port *port; + struct console *cons; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(cons, &pdrvdata.consoles, list) { + port = container_of(cons, struct port, cons); + if (port->in_vq == vq || port->out_vq == vq) + goto out; + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + static void free_buf(struct port_buffer *buf) { kfree(buf->buf); @@ -181,15 +207,67 @@ static void *get_inbuf(struct port *port) * * Callers should take appropriate locks. */ -static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) +static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) { struct scatterlist sg[1]; + int ret; sg_init_one(sg, buf->buf, buf->size); - if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0) - BUG(); + ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf); vq->vq_ops->kick(vq); + return ret; +} + +static bool port_has_data(struct port *port) +{ + unsigned long flags; + bool ret; + + ret = false; + spin_lock_irqsave(&port->inbuf_lock, flags); + if (port->inbuf) + ret = true; + spin_unlock_irqrestore(&port->inbuf_lock, flags); + + return ret; +} + +/* + * Give out the data that's requested from the buffer that we have + * queued up. + */ +static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count) +{ + struct port_buffer *buf; + unsigned long flags; + + if (!out_count || !port_has_data(port)) + return 0; + + buf = port->inbuf; + if (out_count > buf->len - buf->offset) + out_count = buf->len - buf->offset; + + memcpy(out_buf, buf->buf + buf->offset, out_count); + + /* Return the number of bytes actually copied */ + buf->offset += out_count; + + if (buf->offset == buf->len) { + /* + * We're done using all the data in this buffer. + * Re-queue so that the Host can send us more data. + */ + spin_lock_irqsave(&port->inbuf_lock, flags); + port->inbuf = NULL; + + if (add_inbuf(port->in_vq, buf) < 0) + dev_warn(&port->portdev->vdev->dev, "failed add_buf\n"); + + spin_unlock_irqrestore(&port->inbuf_lock, flags); + } + return out_count; } /* @@ -234,9 +312,8 @@ static int put_chars(u32 vtermno, const char *buf, int count) * get_chars() is the callback from the hvc_console infrastructure * when an interrupt is received. * - * Most of the code deals with the fact that the hvc_console() - * infrastructure only asks us for 16 bytes at a time. We keep - * in_offset and in_used fields for partially-filled buffers. + * We call out to fill_readbuf that gets us the required data from the + * buffers that are queued up. */ static int get_chars(u32 vtermno, char *buf, int count) { @@ -249,25 +326,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); - /* No more in buffer? See if they've (re)used it. */ - if (port->inbuf->offset == port->inbuf->len) { - if (!get_inbuf(port)) - return 0; - } - - /* You want more than we have to give? Well, try wanting less! */ - if (port->inbuf->offset + count > port->inbuf->len) - count = port->inbuf->len - port->inbuf->offset; - - /* Copy across to their buffer and increment offset. */ - memcpy(buf, port->inbuf->buf + port->inbuf->offset, count); - port->inbuf->offset += count; - - /* Finished? Re-register buffer so Host will use it again. */ - if (port->inbuf->offset == port->inbuf->len) - add_inbuf(port->in_vq, port->inbuf); - - return count; + return fill_readbuf(port, buf, count); } static void resize_console(struct port *port) @@ -314,13 +373,18 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) static void hvc_handle_input(struct virtqueue *vq) { - struct console *cons; - bool activity = false; + struct port *port; + unsigned long flags; + + port = find_port_by_vq(vq->vdev->priv, vq); + if (!port) + return; - list_for_each_entry(cons, &pdrvdata.consoles, list) - activity |= hvc_poll(cons->hvc); + spin_lock_irqsave(&port->inbuf_lock, flags); + port->inbuf = get_inbuf(port); + spin_unlock_irqrestore(&port->inbuf_lock, flags); - if (activity) + if (hvc_poll(port->cons.hvc)) hvc_kick(); } @@ -388,6 +452,7 @@ int __devinit init_port_console(struct port *port) static int __devinit add_port(struct ports_device *portdev) { struct port *port; + struct port_buffer *inbuf; int err; port = kmalloc(sizeof(*port), GFP_KERNEL); @@ -397,26 +462,31 @@ static int __devinit add_port(struct ports_device *portdev) } port->portdev = portdev; + + port->inbuf = NULL; + port->in_vq = portdev->in_vqs[0]; port->out_vq = portdev->out_vqs[0]; - port->inbuf = alloc_buf(PAGE_SIZE); - if (!port->inbuf) { + spin_lock_init(&port->inbuf_lock); + + inbuf = alloc_buf(PAGE_SIZE); + if (!inbuf) { err = -ENOMEM; goto free_port; } + /* Register the input buffer the first time. */ + add_inbuf(port->in_vq, inbuf); + err = init_port_console(port); if (err) goto free_inbuf; - /* Register the input buffer the first time. */ - add_inbuf(port->in_vq, port->inbuf); - return 0; free_inbuf: - free_buf(port->inbuf); + free_buf(inbuf); free_port: kfree(port); fail: -- cgit v1.2.3 From f997f00bf8c3ddf748d757105afa1a7dd5297208 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 17:28:51 +0530 Subject: virtio: console: Introduce a send_buf function for a common path for sending data to host Adding support for generic ports that will write to userspace will need some code changes. Consolidate the write routine into send_buf() and put_chars() now just calls into the new function. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 50 ++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 5096d92f5b89..d01051060be3 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -233,6 +233,38 @@ static bool port_has_data(struct port *port) return ret; } +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) +{ + struct scatterlist sg[1]; + struct virtqueue *out_vq; + ssize_t ret; + unsigned int len; + + out_vq = port->out_vq; + + sg_init_one(sg, in_buf, in_count); + ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf); + + /* Tell Host to go! */ + out_vq->vq_ops->kick(out_vq); + + if (ret < 0) { + len = 0; + goto fail; + } + + /* + * Wait till the host acknowledges it pushed out the data we + * sent. Also ensure we return to userspace the number of + * bytes that were successfully consumed by the host. + */ + while (!out_vq->vq_ops->get_buf(out_vq, &len)) + cpu_relax(); +fail: + /* We're expected to return the amount of data we wrote */ + return len; +} + /* * Give out the data that's requested from the buffer that we have * queued up. @@ -280,10 +312,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count) */ static int put_chars(u32 vtermno, const char *buf, int count) { - struct scatterlist sg[1]; struct port *port; - struct virtqueue *out_vq; - unsigned int len; port = find_port_by_vtermno(vtermno); if (!port) @@ -292,20 +321,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); - out_vq = port->out_vq; - /* This is a convenient routine to initialize a single-elem sg list */ - sg_init_one(sg, buf, count); - - /* This shouldn't fail: if it does, we lose chars. */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, port) >= 0) { - /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); - while (!out_vq->vq_ops->get_buf(out_vq, &len)) - cpu_relax(); - } - - /* We're expected to return the amount of data we wrote: all of it. */ - return count; + return send_buf(port, (void *)buf, count); } /* -- cgit v1.2.3 From 17634ba25544d60af1968982929150efad755032 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 21:03:25 +0530 Subject: virtio: console: Add a new MULTIPORT feature, support for generic ports This commit adds a new feature, MULTIPORT. If the host supports this feature as well, the config space has the number of ports defined for that device. New ports are spawned according to this information. The config space also has the maximum number of ports that can be spawned for a particular device. This is useful in initializing the appropriate number of virtqueues in advance, as ports might be hot-plugged in later. Using this feature, generic ports can be created which are not tied to hvc consoles. We also open up a private channel between the host and the guest via which some "control" messages are exchanged for the ports, like whether the port being spawned is a console port, resizing the console window, etc. Next commits will add support for hotplugging and presenting char devices in /dev/ for bi-directional guest-host communication. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 392 +++++++++++++++++++++++++++++++++++------ include/linux/virtio_console.h | 21 +++ 2 files changed, 357 insertions(+), 56 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index d01051060be3..a70f2b3a9e64 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation + * Copyright (C) 2009, 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ #include #include #include +#include #include "hvc_console.h" /* @@ -69,17 +71,6 @@ struct console { u32 vtermno; }; -/* - * This is a per-device struct that stores data common to all the - * ports for that device (vdev->priv). - */ -struct ports_device { - /* Array of per-port IO virtqueues */ - struct virtqueue **in_vqs, **out_vqs; - - struct virtio_device *vdev; -}; - struct port_buffer { char *buf; @@ -92,8 +83,46 @@ struct port_buffer { size_t offset; }; +/* + * This is a per-device struct that stores data common to all the + * ports for that device (vdev->priv). + */ +struct ports_device { + /* + * Workqueue handlers where we process deferred work after + * notification + */ + struct work_struct control_work; + + struct list_head ports; + + /* To protect the list of ports */ + spinlock_t ports_lock; + + /* To protect the vq operations for the control channel */ + spinlock_t cvq_lock; + + /* The current config space is stored here */ + struct virtio_console_config config; + + /* The virtio device we're associated with */ + struct virtio_device *vdev; + + /* + * A couple of virtqueues for the control channel: one for + * guest->host transfers, one for host->guest transfers + */ + struct virtqueue *c_ivq, *c_ovq; + + /* Array of per-port IO virtqueues */ + struct virtqueue **in_vqs, **out_vqs; +}; + /* This struct holds the per-port data */ struct port { + /* Next port in the list, head is in the ports_device */ + struct list_head list; + /* Pointer to the parent virtio_console device */ struct ports_device *portdev; @@ -115,6 +144,9 @@ struct port { * hooked up to an hvc console */ struct console cons; + + /* The 'id' to identify the port with the Host */ + u32 id; }; /* This is the very early arch-specified put chars function. */ @@ -139,25 +171,56 @@ out: return port; } +static struct port *find_port_by_id(struct ports_device *portdev, u32 id) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) + if (port->id == id) + goto out; + port = NULL; +out: + spin_unlock_irqrestore(&portdev->ports_lock, flags); + + return port; +} + static struct port *find_port_by_vq(struct ports_device *portdev, struct virtqueue *vq) { struct port *port; - struct console *cons; unsigned long flags; - spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(cons, &pdrvdata.consoles, list) { - port = container_of(cons, struct port, cons); + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) if (port->in_vq == vq || port->out_vq == vq) goto out; - } port = NULL; out: - spin_unlock_irqrestore(&pdrvdata_lock, flags); + spin_unlock_irqrestore(&portdev->ports_lock, flags); return port; } +static bool is_console_port(struct port *port) +{ + if (port->cons.hvc) + return true; + return false; +} + +static inline bool use_multiport(struct ports_device *portdev) +{ + /* + * This condition can be true when put_chars is called from + * early_init + */ + if (!portdev->vdev) + return 0; + return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); +} + static void free_buf(struct port_buffer *buf) { kfree(buf->buf); @@ -233,6 +296,32 @@ static bool port_has_data(struct port *port) return ret; } +static ssize_t send_control_msg(struct port *port, unsigned int event, + unsigned int value) +{ + struct scatterlist sg[1]; + struct virtio_console_control cpkt; + struct virtqueue *vq; + int len; + + if (!use_multiport(port->portdev)) + return 0; + + cpkt.id = port->id; + cpkt.event = event; + cpkt.value = value; + + vq = port->portdev->c_ovq; + + sg_init_one(sg, &cpkt, sizeof(cpkt)); + if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) { + vq->vq_ops->kick(vq); + while (!vq->vq_ops->get_buf(vq, &len)) + cpu_relax(); + } + return 0; +} + static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) { struct scatterlist sg[1]; @@ -387,24 +476,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) hp->irq_requested = 0; } -static void hvc_handle_input(struct virtqueue *vq) -{ - struct port *port; - unsigned long flags; - - port = find_port_by_vq(vq->vdev->priv, vq); - if (!port) - return; - - spin_lock_irqsave(&port->inbuf_lock, flags); - port->inbuf = get_inbuf(port); - spin_unlock_irqrestore(&port->inbuf_lock, flags); - - if (hvc_poll(port->cons.hvc)) - hvc_kick(); -} - -/* The operations for the console. */ +/* The operations for console ports. */ static const struct hv_ops hv_ops = { .get_chars = get_chars, .put_chars = put_chars, @@ -428,7 +500,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) return hvc_instantiate(0, 0, &hv_ops); } -int __devinit init_port_console(struct port *port) +int init_port_console(struct port *port) { int ret; @@ -465,7 +537,122 @@ int __devinit init_port_console(struct port *port) return 0; } -static int __devinit add_port(struct ports_device *portdev) +/* Any private messages that the Host and Guest want to share */ +static void handle_control_message(struct ports_device *portdev, + struct port_buffer *buf) +{ + struct virtio_console_control *cpkt; + struct port *port; + + cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); + + port = find_port_by_id(portdev, cpkt->id); + if (!port) { + /* No valid header at start of buffer. Drop it. */ + dev_dbg(&portdev->vdev->dev, + "Invalid index %u in control packet\n", cpkt->id); + return; + } + + switch (cpkt->event) { + case VIRTIO_CONSOLE_CONSOLE_PORT: + if (!cpkt->value) + break; + if (is_console_port(port)) + break; + + init_port_console(port); + /* + * Could remove the port here in case init fails - but + * have to notify the host first. + */ + break; + case VIRTIO_CONSOLE_RESIZE: + if (!is_console_port(port)) + break; + port->cons.hvc->irq_requested = 1; + resize_console(port); + break; + } +} + +static void control_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + struct virtqueue *vq; + struct port_buffer *buf; + unsigned int len; + + portdev = container_of(work, struct ports_device, control_work); + vq = portdev->c_ivq; + + spin_lock(&portdev->cvq_lock); + while ((buf = vq->vq_ops->get_buf(vq, &len))) { + spin_unlock(&portdev->cvq_lock); + + buf->len = len; + buf->offset = 0; + + handle_control_message(portdev, buf); + + spin_lock(&portdev->cvq_lock); + if (add_inbuf(portdev->c_ivq, buf) < 0) { + dev_warn(&portdev->vdev->dev, + "Error adding buffer to queue\n"); + free_buf(buf); + } + } + spin_unlock(&portdev->cvq_lock); +} + +static void in_intr(struct virtqueue *vq) +{ + struct port *port; + unsigned long flags; + + port = find_port_by_vq(vq->vdev->priv, vq); + if (!port) + return; + + spin_lock_irqsave(&port->inbuf_lock, flags); + port->inbuf = get_inbuf(port); + + spin_unlock_irqrestore(&port->inbuf_lock, flags); + + if (is_console_port(port) && hvc_poll(port->cons.hvc)) + hvc_kick(); +} + +static void control_intr(struct virtqueue *vq) +{ + struct ports_device *portdev; + + portdev = vq->vdev->priv; + schedule_work(&portdev->control_work); +} + +static void fill_queue(struct virtqueue *vq, spinlock_t *lock) +{ + struct port_buffer *buf; + int ret; + + do { + buf = alloc_buf(PAGE_SIZE); + if (!buf) + break; + + spin_lock_irq(lock); + ret = add_inbuf(vq, buf); + if (ret < 0) { + spin_unlock_irq(lock); + free_buf(buf); + break; + } + spin_unlock_irq(lock); + } while (ret > 0); +} + +static int add_port(struct ports_device *portdev, u32 id) { struct port *port; struct port_buffer *inbuf; @@ -478,11 +665,13 @@ static int __devinit add_port(struct ports_device *portdev) } port->portdev = portdev; + port->id = id; port->inbuf = NULL; + port->cons.hvc = NULL; - port->in_vq = portdev->in_vqs[0]; - port->out_vq = portdev->out_vqs[0]; + port->in_vq = portdev->in_vqs[port->id]; + port->out_vq = portdev->out_vqs[port->id]; spin_lock_init(&port->inbuf_lock); @@ -495,9 +684,25 @@ static int __devinit add_port(struct ports_device *portdev) /* Register the input buffer the first time. */ add_inbuf(port->in_vq, inbuf); - err = init_port_console(port); - if (err) - goto free_inbuf; + /* + * If we're not using multiport support, this has to be a console port + */ + if (!use_multiport(port->portdev)) { + err = init_port_console(port); + if (err) + goto free_inbuf; + } + + spin_lock_irq(&portdev->ports_lock); + list_add_tail(&port->list, &port->portdev->ports); + spin_unlock_irq(&portdev->ports_lock); + + /* + * Tell the Host we're set so that it can send us various + * configuration parameters for this port (eg, port name, + * caching, whether this is a console port, etc.) + */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); return 0; @@ -514,12 +719,11 @@ static int init_vqs(struct ports_device *portdev) vq_callback_t **io_callbacks; char **io_names; struct virtqueue **vqs; - u32 nr_ports, nr_queues; + u32 i, j, nr_ports, nr_queues; int err; - /* We currently only have one port and two queues for that port */ - nr_ports = 1; - nr_queues = 2; + nr_ports = portdev->config.max_nr_ports; + nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); if (!vqs) { @@ -549,11 +753,32 @@ static int init_vqs(struct ports_device *portdev) goto free_invqs; } - io_callbacks[0] = hvc_handle_input; - io_callbacks[1] = NULL; - io_names[0] = "input"; - io_names[1] = "output"; - + /* + * For backward compat (newer host but older guest), the host + * spawns a console port first and also inits the vqs for port + * 0 before others. + */ + j = 0; + io_callbacks[j] = in_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "input"; + io_names[j + 1] = "output"; + j += 2; + + if (use_multiport(portdev)) { + io_callbacks[j] = control_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "control-i"; + io_names[j + 1] = "control-o"; + + for (i = 1; i < nr_ports; i++) { + j += 2; + io_callbacks[j] = in_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "input"; + io_names[j + 1] = "output"; + } + } /* Find the queues. */ err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, io_callbacks, @@ -561,9 +786,20 @@ static int init_vqs(struct ports_device *portdev) if (err) goto free_outvqs; + j = 0; portdev->in_vqs[0] = vqs[0]; portdev->out_vqs[0] = vqs[1]; - + j += 2; + if (use_multiport(portdev)) { + portdev->c_ivq = vqs[j]; + portdev->c_ovq = vqs[j + 1]; + + for (i = 1; i < nr_ports; i++) { + j += 2; + portdev->in_vqs[i] = vqs[j]; + portdev->out_vqs[i] = vqs[j + 1]; + } + } kfree(io_callbacks); kfree(io_names); kfree(vqs); @@ -587,11 +823,17 @@ fail: /* * Once we're further in boot, we get probed like any other virtio * device. + * + * If the host also supports multiple console ports, we check the + * config space to see how many ports the host has spawned. We + * initialize each port found. */ static int __devinit virtcons_probe(struct virtio_device *vdev) { struct ports_device *portdev; + u32 i; int err; + bool multiport; portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); if (!portdev) { @@ -603,16 +845,53 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) portdev->vdev = vdev; vdev->priv = portdev; + multiport = false; + portdev->config.nr_ports = 1; + portdev->config.max_nr_ports = 1; + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { + multiport = true; + vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; + + vdev->config->get(vdev, offsetof(struct virtio_console_config, + nr_ports), + &portdev->config.nr_ports, + sizeof(portdev->config.nr_ports)); + vdev->config->get(vdev, offsetof(struct virtio_console_config, + max_nr_ports), + &portdev->config.max_nr_ports, + sizeof(portdev->config.max_nr_ports)); + if (portdev->config.nr_ports > portdev->config.max_nr_ports) { + dev_warn(&vdev->dev, + "More ports (%u) specified than allowed (%u). Will init %u ports.", + portdev->config.nr_ports, + portdev->config.max_nr_ports, + portdev->config.max_nr_ports); + + portdev->config.nr_ports = portdev->config.max_nr_ports; + } + } + + /* Let the Host know we support multiple ports.*/ + vdev->config->finalize_features(vdev); + err = init_vqs(portdev); if (err < 0) { dev_err(&vdev->dev, "Error %d initializing vqs\n", err); goto free; } - /* We only have one port. */ - err = add_port(portdev); - if (err) - goto free_vqs; + spin_lock_init(&portdev->ports_lock); + INIT_LIST_HEAD(&portdev->ports); + + if (multiport) { + spin_lock_init(&portdev->cvq_lock); + INIT_WORK(&portdev->control_work, &control_work_handler); + + fill_queue(portdev->c_ivq, &portdev->cvq_lock); + } + + for (i = 0; i < portdev->config.nr_ports; i++) + add_port(portdev, i); /* Start using the new console output. */ early_put_chars = NULL; @@ -635,6 +914,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_CONSOLE_F_SIZE, + VIRTIO_CONSOLE_F_MULTIPORT, }; static struct virtio_driver virtio_console = { diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 9e0da40beae0..f4d183b5b493 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -6,18 +6,39 @@ /* * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so * anyone can use the definitions to implement compatible drivers/servers. + * + * Copyright (C) Red Hat, Inc., 2009, 2010 */ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ struct virtio_console_config { /* colums of the screens */ __u16 cols; /* rows of the screens */ __u16 rows; + /* max. number of ports this device can hold */ + __u32 max_nr_ports; + /* number of ports added so far */ + __u32 nr_ports; } __attribute__((packed)); +/* + * A message that's passed between the Host and the Guest for a + * particular port. + */ +struct virtio_console_control { + __u32 id; /* Port number */ + __u16 event; /* The kind of control event (see below) */ + __u16 value; /* Extra information for the key */ +}; + +/* Some events for control messages */ +#define VIRTIO_CONSOLE_PORT_READY 0 +#define VIRTIO_CONSOLE_CONSOLE_PORT 1 +#define VIRTIO_CONSOLE_RESIZE 2 #ifdef __KERNEL__ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); -- cgit v1.2.3 From b766ceed5bbf04ae153389f5a15f53b9b6106a35 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 21:26:45 +0530 Subject: virtio: console: Prepare for writing to userspace buffers When ports get advertised as char devices, the buffers will come from userspace. Equip the fill_readbuf function with the ability to write to userspace buffers. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a70f2b3a9e64..8e447e1e12bc 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -358,7 +358,8 @@ fail: * Give out the data that's requested from the buffer that we have * queued up. */ -static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count) +static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, + bool to_user) { struct port_buffer *buf; unsigned long flags; @@ -367,12 +368,18 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count) return 0; buf = port->inbuf; - if (out_count > buf->len - buf->offset) - out_count = buf->len - buf->offset; + out_count = min(out_count, buf->len - buf->offset); - memcpy(out_buf, buf->buf + buf->offset, out_count); + if (to_user) { + ssize_t ret; + + ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count); + if (ret) + return -EFAULT; + } else { + memcpy(out_buf, buf->buf + buf->offset, out_count); + } - /* Return the number of bytes actually copied */ buf->offset += out_count; if (buf->offset == buf->len) { @@ -388,6 +395,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count) spin_unlock_irqrestore(&port->inbuf_lock, flags); } + /* Return the number of bytes actually copied */ return out_count; } @@ -431,7 +439,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); - return fill_readbuf(port, buf, count); + return fill_readbuf(port, buf, count, false); } static void resize_console(struct port *port) -- cgit v1.2.3 From fb08bd274df61967f40d49c4625fe6ed75a69ab5 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 21:36:04 +0530 Subject: virtio: console: Associate each port with a char device The char device will be used as an interface by applications on the guest to communicate with apps on the host. The devices created are placed in /dev/vportNpn where N is the virtio-console device number and n is the port number for that device. One dynamic major device number is allocated for each device and minor numbers are allocated for the ports contained within that device. The file operation for the char devs will be added in the following commits. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/Kconfig | 8 +++++ drivers/char/virtio_console.c | 81 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index e023682be2c4..3141dd3b6e53 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -666,6 +666,14 @@ config VIRTIO_CONSOLE help Virtio console for use with lguest and other hypervisors. + Also serves as a general-purpose serial device for data + transfer between the guest and host. Character devices at + /dev/vportNpn will be created when corresponding ports are + found, where N is the device number and n is the port number + within that device. If specified by the host, a sysfs + attribute called 'name' will be populated with a name for + the port which can be used by udev scripts to create a + symlink to the device. config HVCS tristate "IBM Hypervisor Virtual Console Server support" diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 8e447e1e12bc..64ef476d6557 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -16,6 +16,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include #include #include #include @@ -34,6 +36,12 @@ * across multiple devices and multiple ports per device. */ struct ports_driver_data { + /* Used for registering chardevs */ + struct class *class; + + /* Number of devices this driver is handling */ + unsigned int index; + /* * This is used to keep track of the number of hvc consoles * spawned by this driver. This number is given as the first @@ -116,6 +124,12 @@ struct ports_device { /* Array of per-port IO virtqueues */ struct virtqueue **in_vqs, **out_vqs; + + /* Used for numbering devices for sysfs and debugfs */ + unsigned int drv_index; + + /* Major number for this device. Ports will be created as minors. */ + int chr_major; }; /* This struct holds the per-port data */ @@ -145,6 +159,10 @@ struct port { */ struct console cons; + /* Each port associates with a separate char device */ + struct cdev cdev; + struct device *dev; + /* The 'id' to identify the port with the Host */ u32 id; }; @@ -391,7 +409,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, port->inbuf = NULL; if (add_inbuf(port->in_vq, buf) < 0) - dev_warn(&port->portdev->vdev->dev, "failed add_buf\n"); + dev_warn(port->dev, "failed add_buf\n"); spin_unlock_irqrestore(&port->inbuf_lock, flags); } @@ -664,6 +682,7 @@ static int add_port(struct ports_device *portdev, u32 id) { struct port *port; struct port_buffer *inbuf; + dev_t devt; int err; port = kmalloc(sizeof(*port), GFP_KERNEL); @@ -681,12 +700,32 @@ static int add_port(struct ports_device *portdev, u32 id) port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; + cdev_init(&port->cdev, NULL); + + devt = MKDEV(portdev->chr_major, id); + err = cdev_add(&port->cdev, devt, 1); + if (err < 0) { + dev_err(&port->portdev->vdev->dev, + "Error %d adding cdev for port %u\n", err, id); + goto free_port; + } + port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + devt, port, "vport%up%u", + port->portdev->drv_index, id); + if (IS_ERR(port->dev)) { + err = PTR_ERR(port->dev); + dev_err(&port->portdev->vdev->dev, + "Error %d creating device for port %u\n", + err, id); + goto free_cdev; + } + spin_lock_init(&port->inbuf_lock); inbuf = alloc_buf(PAGE_SIZE); if (!inbuf) { err = -ENOMEM; - goto free_port; + goto free_device; } /* Register the input buffer the first time. */ @@ -716,6 +755,10 @@ static int add_port(struct ports_device *portdev, u32 id) free_inbuf: free_buf(inbuf); +free_device: + device_destroy(pdrvdata.class, port->dev->devt); +free_cdev: + cdev_del(&port->cdev); free_port: kfree(port); fail: @@ -828,6 +871,10 @@ fail: return err; } +static const struct file_operations portdev_fops = { + .owner = THIS_MODULE, +}; + /* * Once we're further in boot, we get probed like any other virtio * device. @@ -853,6 +900,20 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) portdev->vdev = vdev; vdev->priv = portdev; + spin_lock_irq(&pdrvdata_lock); + portdev->drv_index = pdrvdata.index++; + spin_unlock_irq(&pdrvdata_lock); + + portdev->chr_major = register_chrdev(0, "virtio-portsdev", + &portdev_fops); + if (portdev->chr_major < 0) { + dev_err(&vdev->dev, + "Error %d registering chrdev for device %u\n", + portdev->chr_major, portdev->drv_index); + err = portdev->chr_major; + goto free; + } + multiport = false; portdev->config.nr_ports = 1; portdev->config.max_nr_ports = 1; @@ -885,7 +946,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) err = init_vqs(portdev); if (err < 0) { dev_err(&vdev->dev, "Error %d initializing vqs\n", err); - goto free; + goto free_chrdev; } spin_lock_init(&portdev->ports_lock); @@ -905,10 +966,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) early_put_chars = NULL; return 0; -free_vqs: - vdev->config->del_vqs(vdev); - kfree(portdev->in_vqs); - kfree(portdev->out_vqs); +free_chrdev: + unregister_chrdev(portdev->chr_major, "virtio-portsdev"); free: kfree(portdev); fail: @@ -937,6 +996,14 @@ static struct virtio_driver virtio_console = { static int __init init(void) { + int err; + + pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); + if (IS_ERR(pdrvdata.class)) { + err = PTR_ERR(pdrvdata.class); + pr_err("Error %d creating virtio-ports class\n", err); + return err; + } INIT_LIST_HEAD(&pdrvdata.consoles); return register_virtio_driver(&virtio_console); -- cgit v1.2.3 From 2030fa496d74b49220308eaccf656e2338019cfd Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 21:49:30 +0530 Subject: virtio: console: Add file operations to ports for open/read/write/poll Allow guest userspace applications to open, read from, write to, poll the ports via the char dev interface. When a port gets opened, a notification is sent to the host via a control message indicating a connection has been established. Similarly, on closing of the port, a notification is sent indicating disconnection. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 164 ++++++++++++++++++++++++++++++++++++++++- include/linux/virtio_console.h | 1 + 2 files changed, 164 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 64ef476d6557..ece1546fbb20 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -19,11 +19,15 @@ #include #include #include +#include #include #include +#include +#include #include #include #include +#include #include #include "hvc_console.h" @@ -163,8 +167,14 @@ struct port { struct cdev cdev; struct device *dev; + /* A waitqueue for poll() or blocking read operations */ + wait_queue_head_t waitqueue; + /* The 'id' to identify the port with the Host */ u32 id; + + /* Is the host device open */ + bool host_connected; }; /* This is the very early arch-specified put chars function. */ @@ -417,6 +427,146 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, return out_count; } +/* The condition that must be true for polling to end */ +static bool wait_is_over(struct port *port) +{ + return port_has_data(port) || !port->host_connected; +} + +static ssize_t port_fops_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + ssize_t ret; + + port = filp->private_data; + + if (!port_has_data(port)) { + /* + * If nothing's connected on the host just return 0 in + * case of list_empty; this tells the userspace app + * that there's no connection + */ + if (!port->host_connected) + return 0; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + wait_is_over(port)); + if (ret < 0) + return ret; + } + /* + * We could've received a disconnection message while we were + * waiting for more data. + * + * This check is not clubbed in the if() statement above as we + * might receive some data as well as the host could get + * disconnected after we got woken up from our wait. So we + * really want to give off whatever data we have and only then + * check for host_connected. + */ + if (!port_has_data(port) && !port->host_connected) + return 0; + + return fill_readbuf(port, ubuf, count, true); +} + +static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + char *buf; + ssize_t ret; + + port = filp->private_data; + + count = min((size_t)(32 * 1024), count); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = copy_from_user(buf, ubuf, count); + if (ret) { + ret = -EFAULT; + goto free_buf; + } + + ret = send_buf(port, buf, count); +free_buf: + kfree(buf); + return ret; +} + +static unsigned int port_fops_poll(struct file *filp, poll_table *wait) +{ + struct port *port; + unsigned int ret; + + port = filp->private_data; + poll_wait(filp, &port->waitqueue, wait); + + ret = 0; + if (port->inbuf) + ret |= POLLIN | POLLRDNORM; + if (port->host_connected) + ret |= POLLOUT; + if (!port->host_connected) + ret |= POLLHUP; + + return ret; +} + +static int port_fops_release(struct inode *inode, struct file *filp) +{ + struct port *port; + + port = filp->private_data; + + /* Notify host of port being closed */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + + return 0; +} + +static int port_fops_open(struct inode *inode, struct file *filp) +{ + struct cdev *cdev = inode->i_cdev; + struct port *port; + + port = container_of(cdev, struct port, cdev); + filp->private_data = port; + + /* + * Don't allow opening of console port devices -- that's done + * via /dev/hvc + */ + if (is_console_port(port)) + return -ENXIO; + + /* Notify host of port being opened */ + send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); + + return 0; +} + +/* + * The file operations that we support: programs in the guest can open + * a console device, read from it, write to it, poll for data and + * close it. The devices are at + * /dev/vportp + */ +static const struct file_operations port_fops = { + .owner = THIS_MODULE, + .open = port_fops_open, + .read = port_fops_read, + .write = port_fops_write, + .poll = port_fops_poll, + .release = port_fops_release, +}; + /* * The put_chars() callback is pretty straightforward. * @@ -560,6 +710,9 @@ int init_port_console(struct port *port) list_add_tail(&port->cons.list, &pdrvdata.consoles); spin_unlock_irq(&pdrvdata_lock); + /* Notify host of port being opened */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); + return 0; } @@ -599,6 +752,10 @@ static void handle_control_message(struct ports_device *portdev, port->cons.hvc->irq_requested = 1; resize_console(port); break; + case VIRTIO_CONSOLE_PORT_OPEN: + port->host_connected = cpkt->value; + wake_up_interruptible(&port->waitqueue); + break; } } @@ -645,6 +802,8 @@ static void in_intr(struct virtqueue *vq) spin_unlock_irqrestore(&port->inbuf_lock, flags); + wake_up_interruptible(&port->waitqueue); + if (is_console_port(port) && hvc_poll(port->cons.hvc)) hvc_kick(); } @@ -697,10 +856,12 @@ static int add_port(struct ports_device *portdev, u32 id) port->inbuf = NULL; port->cons.hvc = NULL; + port->host_connected = false; + port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; - cdev_init(&port->cdev, NULL); + cdev_init(&port->cdev, &port_fops); devt = MKDEV(portdev->chr_major, id); err = cdev_add(&port->cdev, devt, 1); @@ -721,6 +882,7 @@ static int add_port(struct ports_device *portdev, u32 id) } spin_lock_init(&port->inbuf_lock); + init_waitqueue_head(&port->waitqueue); inbuf = alloc_buf(PAGE_SIZE); if (!inbuf) { diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index f4d183b5b493..bd0e2a596f93 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -39,6 +39,7 @@ struct virtio_console_control { #define VIRTIO_CONSOLE_PORT_READY 0 #define VIRTIO_CONSOLE_CONSOLE_PORT 1 #define VIRTIO_CONSOLE_RESIZE 2 +#define VIRTIO_CONSOLE_PORT_OPEN 3 #ifdef __KERNEL__ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); -- cgit v1.2.3 From 3c7969ccb569968a79fab3729075751bc8fc2f78 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 26 Nov 2009 11:25:38 +0530 Subject: virtio: console: Ensure only one process can have a port open at a time Add a guest_connected field that ensures only one process can have a port open at a time. This also ensures we don't have a race when we later add support for dropping buffers when closing the char dev and buffer caching is turned off for the particular port. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index ece1546fbb20..5e3503a31312 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -175,6 +175,9 @@ struct port { /* Is the host device open */ bool host_connected; + + /* We should allow only one process to open a port */ + bool guest_connected; }; /* This is the very early arch-specified put chars function. */ @@ -528,6 +531,8 @@ static int port_fops_release(struct inode *inode, struct file *filp) /* Notify host of port being closed */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + port->guest_connected = false; + return 0; } @@ -546,6 +551,16 @@ static int port_fops_open(struct inode *inode, struct file *filp) if (is_console_port(port)) return -ENXIO; + /* Allow only one process to open a particular port at a time */ + spin_lock_irq(&port->inbuf_lock); + if (port->guest_connected) { + spin_unlock_irq(&port->inbuf_lock); + return -EMFILE; + } + + port->guest_connected = true; + spin_unlock_irq(&port->inbuf_lock); + /* Notify host of port being opened */ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -709,6 +724,7 @@ int init_port_console(struct port *port) pdrvdata.next_vtermno++; list_add_tail(&port->cons.list, &pdrvdata.consoles); spin_unlock_irq(&pdrvdata_lock); + port->guest_connected = true; /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -856,7 +872,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->inbuf = NULL; port->cons.hvc = NULL; - port->host_connected = false; + port->host_connected = port->guest_connected = false; port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; -- cgit v1.2.3 From 431edb8a8bca71008fefceadf53b9315ef7196ec Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 21:57:40 +0530 Subject: virtio: console: Register with sysfs and create a 'name' attribute for ports The host can set a name for ports so that they're easily discoverable instead of going by the /dev/vportNpn naming. This attribute will be placed in /sys/class/virtio-ports/vportNpn/name. udev scripts can then create symlinks to the port using the name. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 57 ++++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_console.h | 1 + 2 files changed, 58 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 5e3503a31312..99d182b132c4 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -170,6 +170,9 @@ struct port { /* A waitqueue for poll() or blocking read operations */ wait_queue_head_t waitqueue; + /* The 'name' of the port that we expose via sysfs properties */ + char *name; + /* The 'id' to identify the port with the Host */ u32 id; @@ -732,12 +735,36 @@ int init_port_console(struct port *port) return 0; } +static ssize_t show_port_name(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct port *port; + + port = dev_get_drvdata(dev); + + return sprintf(buffer, "%s\n", port->name); +} + +static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL); + +static struct attribute *port_sysfs_entries[] = { + &dev_attr_name.attr, + NULL +}; + +static struct attribute_group port_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = port_sysfs_entries, +}; + /* Any private messages that the Host and Guest want to share */ static void handle_control_message(struct ports_device *portdev, struct port_buffer *buf) { struct virtio_console_control *cpkt; struct port *port; + size_t name_size; + int err; cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); @@ -772,6 +799,35 @@ static void handle_control_message(struct ports_device *portdev, port->host_connected = cpkt->value; wake_up_interruptible(&port->waitqueue); break; + case VIRTIO_CONSOLE_PORT_NAME: + /* + * Skip the size of the header and the cpkt to get the size + * of the name that was sent + */ + name_size = buf->len - buf->offset - sizeof(*cpkt) + 1; + + port->name = kmalloc(name_size, GFP_KERNEL); + if (!port->name) { + dev_err(port->dev, + "Not enough space to store port name\n"); + break; + } + strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), + name_size - 1); + port->name[name_size - 1] = 0; + + /* + * Since we only have one sysfs attribute, 'name', + * create it only if we have a name for the port. + */ + err = sysfs_create_group(&port->dev->kobj, + &port_attribute_group); + if (err) + dev_err(port->dev, + "Error %d creating sysfs device attributes\n", + err); + + break; } } @@ -869,6 +925,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->portdev = portdev; port->id = id; + port->name = NULL; port->inbuf = NULL; port->cons.hvc = NULL; diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index bd0e2a596f93..1ebf007812a8 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -40,6 +40,7 @@ struct virtio_console_control { #define VIRTIO_CONSOLE_CONSOLE_PORT 1 #define VIRTIO_CONSOLE_RESIZE 2 #define VIRTIO_CONSOLE_PORT_OPEN 3 +#define VIRTIO_CONSOLE_PORT_NAME 4 #ifdef __KERNEL__ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); -- cgit v1.2.3 From 88f251ac58b2460ed16ff619a020ad3ef365e607 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 22:15:30 +0530 Subject: virtio: console: Remove cached data on port close Remove any data that we might have in a port's inbuf when closing a port or when any data is received when a port is closed. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 99d182b132c4..5506ff8bdf03 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -316,6 +316,31 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) return ret; } +/* Discard any unread data this port has. Callers lockers. */ +static void discard_port_data(struct port *port) +{ + struct port_buffer *buf; + struct virtqueue *vq; + unsigned int len; + + vq = port->in_vq; + if (port->inbuf) + buf = port->inbuf; + else + buf = vq->vq_ops->get_buf(vq, &len); + + if (!buf) + return; + + if (add_inbuf(vq, buf) < 0) { + buf->len = buf->offset = 0; + dev_warn(port->dev, "Error adding buffer back to vq\n"); + return; + } + + port->inbuf = NULL; +} + static bool port_has_data(struct port *port) { unsigned long flags; @@ -534,8 +559,13 @@ static int port_fops_release(struct inode *inode, struct file *filp) /* Notify host of port being closed */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + spin_lock_irq(&port->inbuf_lock); port->guest_connected = false; + discard_port_data(port); + + spin_unlock_irq(&port->inbuf_lock); + return 0; } @@ -872,6 +902,16 @@ static void in_intr(struct virtqueue *vq) spin_lock_irqsave(&port->inbuf_lock, flags); port->inbuf = get_inbuf(port); + /* + * Don't queue up data when port is closed. This condition + * can be reached when a console port is not yet connected (no + * tty is spawned) and the host sends out data to console + * ports. For generic serial ports, the host won't + * (shouldn't) send data till the guest is connected. + */ + if (!port->guest_connected) + discard_port_data(port); + spin_unlock_irqrestore(&port->inbuf_lock, flags); wake_up_interruptible(&port->waitqueue); -- cgit v1.2.3 From 7f5d810dac70214d00b2440787535b6c7a73b6b7 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 22:22:08 +0530 Subject: virtio: console: Handle port hot-plug If the 'nr_ports' variable in the config space is updated to a higher value, that means new ports have been hotplugged. Introduce a new workqueue to handle such updates and create new ports. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 78 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 5506ff8bdf03..7c53f58c87ba 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -105,6 +105,7 @@ struct ports_device { * notification */ struct work_struct control_work; + struct work_struct config_work; struct list_head ports; @@ -675,11 +676,6 @@ static void resize_console(struct port *port) } } -static void virtcons_apply_config(struct virtio_device *vdev) -{ - resize_console(find_port_by_vtermno(0)); -} - /* We set the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { @@ -928,6 +924,24 @@ static void control_intr(struct virtqueue *vq) schedule_work(&portdev->control_work); } +static void config_intr(struct virtio_device *vdev) +{ + struct ports_device *portdev; + + portdev = vdev->priv; + if (use_multiport(portdev)) { + /* Handle port hot-add */ + schedule_work(&portdev->config_work); + } + /* + * We'll use this way of resizing only for legacy support. + * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use + * control messages to indicate console size changes so that + * it can be done per-port + */ + resize_console(find_port_by_id(portdev, 0)); +} + static void fill_queue(struct virtqueue *vq, spinlock_t *lock) { struct port_buffer *buf; @@ -1040,6 +1054,57 @@ fail: return err; } +/* + * The workhandler for config-space updates. + * + * This is called when ports are hot-added. + */ +static void config_work_handler(struct work_struct *work) +{ + struct virtio_console_config virtconconf; + struct ports_device *portdev; + struct virtio_device *vdev; + int err; + + portdev = container_of(work, struct ports_device, config_work); + + vdev = portdev->vdev; + vdev->config->get(vdev, + offsetof(struct virtio_console_config, nr_ports), + &virtconconf.nr_ports, + sizeof(virtconconf.nr_ports)); + + if (portdev->config.nr_ports == virtconconf.nr_ports) { + /* + * Port 0 got hot-added. Since we already did all the + * other initialisation for it, just tell the Host + * that the port is ready. + */ + struct port *port; + + port = find_port_by_id(portdev, 0); + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + return; + } + if (virtconconf.nr_ports > portdev->config.max_nr_ports) { + dev_warn(&vdev->dev, + "More ports specified (%u) than allowed (%u)", + portdev->config.nr_ports + 1, + portdev->config.max_nr_ports); + return; + } + if (virtconconf.nr_ports < portdev->config.nr_ports) + return; + + /* Hot-add ports */ + while (virtconconf.nr_ports - portdev->config.nr_ports) { + err = add_port(portdev, portdev->config.nr_ports); + if (err) + break; + portdev->config.nr_ports++; + } +} + static int init_vqs(struct ports_device *portdev) { vq_callback_t **io_callbacks; @@ -1230,6 +1295,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) if (multiport) { spin_lock_init(&portdev->cvq_lock); INIT_WORK(&portdev->control_work, &control_work_handler); + INIT_WORK(&portdev->config_work, &config_work_handler); fill_queue(portdev->c_ivq, &portdev->cvq_lock); } @@ -1266,7 +1332,7 @@ static struct virtio_driver virtio_console = { .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtcons_probe, - .config_changed = virtcons_apply_config, + .config_changed = config_intr, }; static int __init init(void) -- cgit v1.2.3 From 1f7aa42d166cd104b0700d61efe2064178a3f6da Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 22:27:31 +0530 Subject: virtio: console: Add ability to hot-unplug ports Remove port data; deregister from the hvc core if it's a console port. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 65 ++++++++++++++++++++++++++++++++++++++++-- include/linux/virtio_console.h | 1 + 2 files changed, 64 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 7c53f58c87ba..9f20fda9c56f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -783,6 +783,36 @@ static struct attribute_group port_attribute_group = { .attrs = port_sysfs_entries, }; +/* Remove all port-specific data. */ +static int remove_port(struct port *port) +{ + spin_lock_irq(&port->portdev->ports_lock); + list_del(&port->list); + spin_unlock_irq(&port->portdev->ports_lock); + + if (is_console_port(port)) { + spin_lock_irq(&pdrvdata_lock); + list_del(&port->cons.list); + spin_unlock_irq(&pdrvdata_lock); + hvc_remove(port->cons.hvc); + } + if (port->guest_connected) + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + + while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq)) + ; + + sysfs_remove_group(&port->dev->kobj, &port_attribute_group); + device_destroy(pdrvdata.class, port->dev->devt); + cdev_del(&port->cdev); + + discard_port_data(port); + kfree(port->name); + + kfree(port); + return 0; +} + /* Any private messages that the Host and Guest want to share */ static void handle_control_message(struct ports_device *portdev, struct port_buffer *buf) @@ -854,6 +884,32 @@ static void handle_control_message(struct ports_device *portdev, err); break; + case VIRTIO_CONSOLE_PORT_REMOVE: + /* + * Hot unplug the port. We don't decrement nr_ports + * since we don't want to deal with extra complexities + * of using the lowest-available port id: We can just + * pick up the nr_ports number as the id and not have + * userspace send it to us. This helps us in two + * ways: + * + * - We don't need to have a 'port_id' field in the + * config space when a port is hot-added. This is a + * good thing as we might queue up multiple hotplug + * requests issued in our workqueue. + * + * - Another way to deal with this would have been to + * use a bitmap of the active ports and select the + * lowest non-active port from that map. That + * bloats the already tight config space and we + * would end up artificially limiting the + * max. number of ports to sizeof(bitmap). Right + * now we can support 2^32 ports (as the port id is + * stored in a u32 type). + * + */ + remove_port(port); + break; } } @@ -1078,12 +1134,17 @@ static void config_work_handler(struct work_struct *work) /* * Port 0 got hot-added. Since we already did all the * other initialisation for it, just tell the Host - * that the port is ready. + * that the port is ready if we find the port. In + * case the port was hot-removed earlier, we call + * add_port to add the port. */ struct port *port; port = find_port_by_id(portdev, 0); - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + if (!port) + add_port(portdev, 0); + else + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); return; } if (virtconconf.nr_ports > portdev->config.max_nr_ports) { diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 1ebf007812a8..ae4f039515b4 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -41,6 +41,7 @@ struct virtio_console_control { #define VIRTIO_CONSOLE_RESIZE 2 #define VIRTIO_CONSOLE_PORT_OPEN 3 #define VIRTIO_CONSOLE_PORT_NAME 4 +#define VIRTIO_CONSOLE_PORT_REMOVE 5 #ifdef __KERNEL__ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); -- cgit v1.2.3 From d99393effd76571cf2c4a07cbb6e86d41855a8fa Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 21 Dec 2009 22:36:21 +0530 Subject: virtio: console: Add debugfs files for each port to expose debug info This is helpful in examining ports' state. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 9f20fda9c56f..2a04daa9189e 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include @@ -43,6 +44,9 @@ struct ports_driver_data { /* Used for registering chardevs */ struct class *class; + /* Used for exporting per-port information to debugfs */ + struct dentry *debugfs_dir; + /* Number of devices this driver is handling */ unsigned int index; @@ -158,6 +162,9 @@ struct port { /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; + /* File in the debugfs directory that exposes this port's information */ + struct dentry *debugfs_file; + /* * The entries in this struct will be valid if this port is * hooked up to an hvc console @@ -783,6 +790,49 @@ static struct attribute_group port_attribute_group = { .attrs = port_sysfs_entries, }; +static int debugfs_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t debugfs_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + char *buf; + ssize_t ret, out_offset, out_count; + + out_count = 1024; + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + port = filp->private_data; + out_offset = 0; + out_offset += snprintf(buf + out_offset, out_count, + "name: %s\n", port->name ? port->name : ""); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "guest_connected: %d\n", port->guest_connected); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "host_connected: %d\n", port->host_connected); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "is_console: %s\n", + is_console_port(port) ? "yes" : "no"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "console_vtermno: %u\n", port->cons.vtermno); + + ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); + kfree(buf); + return ret; +} + +static const struct file_operations port_debugfs_ops = { + .owner = THIS_MODULE, + .open = debugfs_open, + .read = debugfs_read, +}; + /* Remove all port-specific data. */ static int remove_port(struct port *port) { @@ -809,6 +859,8 @@ static int remove_port(struct port *port) discard_port_data(port); kfree(port->name); + debugfs_remove(port->debugfs_file); + kfree(port); return 0; } @@ -1021,6 +1073,7 @@ static void fill_queue(struct virtqueue *vq, spinlock_t *lock) static int add_port(struct ports_device *portdev, u32 id) { + char debugfs_name[16]; struct port *port; struct port_buffer *inbuf; dev_t devt; @@ -1096,6 +1149,18 @@ static int add_port(struct ports_device *portdev, u32 id) */ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + if (pdrvdata.debugfs_dir) { + /* + * Finally, create the debugfs file that we can use to + * inspect a port's state at any time + */ + sprintf(debugfs_name, "vport%up%u", + port->portdev->drv_index, id); + port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + pdrvdata.debugfs_dir, + port, + &port_debugfs_ops); + } return 0; free_inbuf: @@ -1406,6 +1471,12 @@ static int __init init(void) pr_err("Error %d creating virtio-ports class\n", err); return err; } + + pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); + if (!pdrvdata.debugfs_dir) { + pr_warning("Error %ld creating debugfs dir for virtio-ports\n", + PTR_ERR(pdrvdata.debugfs_dir)); + } INIT_LIST_HEAD(&pdrvdata.consoles); return register_virtio_driver(&virtio_console); -- cgit v1.2.3 From 298add723aecd7af461319fe815d935ef2c40d78 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 18 Jan 2010 16:35:23 +0530 Subject: virtio: console: show error message if hvc_alloc fails for console ports Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2a04daa9189e..a3f1f73bac3a 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -753,6 +753,8 @@ int init_port_console(struct port *port) port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); if (IS_ERR(port->cons.hvc)) { ret = PTR_ERR(port->cons.hvc); + dev_err(port->dev, + "error %d allocating hvc for port\n", ret); port->cons.hvc = NULL; return ret; } -- cgit v1.2.3 From a9cdd4855738906043b8131cfe8055d6cde88ffe Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Fri, 12 Feb 2010 10:32:15 +0530 Subject: virtio: console: Ensure no memleaks in case of unused buffers If unused data exists in in_vq, ensure we flush that first and then detach unused buffers, which will ensure all buffers from the in_vq are removed. Also ensure we free the buffers after detaching them. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a3f1f73bac3a..69d2e616dd0c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -838,6 +838,8 @@ static const struct file_operations port_debugfs_ops = { /* Remove all port-specific data. */ static int remove_port(struct port *port) { + struct port_buffer *buf; + spin_lock_irq(&port->portdev->ports_lock); list_del(&port->list); spin_unlock_irq(&port->portdev->ports_lock); @@ -851,14 +853,17 @@ static int remove_port(struct port *port) if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); - while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq)) - ; - sysfs_remove_group(&port->dev->kobj, &port_attribute_group); device_destroy(pdrvdata.class, port->dev->devt); cdev_del(&port->cdev); + /* Remove unused data this port might have received. */ discard_port_data(port); + + /* Remove buffers we queued up for the Host to send us data in. */ + while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) + free_buf(buf); + kfree(port->name); debugfs_remove(port->debugfs_file); -- cgit v1.2.3 From 7177876fea8306a6f49400d11f5913bf9b3b5e5f Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Fri, 12 Feb 2010 10:32:16 +0530 Subject: virtio: console: Add ability to remove module Add the ability to remove the virtio_console module. This aids debugging. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 69d2e616dd0c..0057bae2036f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1448,6 +1448,36 @@ fail: return err; } +static void virtcons_remove(struct virtio_device *vdev) +{ + struct ports_device *portdev; + struct port *port, *port2; + struct port_buffer *buf; + unsigned int len; + + portdev = vdev->priv; + + cancel_work_sync(&portdev->control_work); + cancel_work_sync(&portdev->config_work); + + list_for_each_entry_safe(port, port2, &portdev->ports, list) + remove_port(port); + + unregister_chrdev(portdev->chr_major, "virtio-portsdev"); + + while ((buf = portdev->c_ivq->vq_ops->get_buf(portdev->c_ivq, &len))) + free_buf(buf); + + while ((buf = portdev->c_ivq->vq_ops->detach_unused_buf(portdev->c_ivq))) + free_buf(buf); + + vdev->config->del_vqs(vdev); + kfree(portdev->in_vqs); + kfree(portdev->out_vqs); + + kfree(portdev); +} + static struct virtio_device_id id_table[] = { { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -1465,6 +1495,7 @@ static struct virtio_driver virtio_console = { .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtcons_probe, + .remove = virtcons_remove, .config_changed = config_intr, }; @@ -1488,7 +1519,17 @@ static int __init init(void) return register_virtio_driver(&virtio_console); } + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtio_console); + + class_destroy(pdrvdata.class); + if (pdrvdata.debugfs_dir) + debugfs_remove_recursive(pdrvdata.debugfs_dir); +} module_init(init); +module_exit(fini); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio console driver"); -- cgit v1.2.3 From 22a29eacd2a17f22c8260a8106a4e36bae7fb6ea Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Fri, 12 Feb 2010 10:32:17 +0530 Subject: virtio: console: Error out if we can't allocate buffers for control queue With MULTIPORT support, the control queue is an integral part of the functioning of the device. If we can't get any buffers allocated, the host won't be able to relay important information and the device may not function as intended. Ensure 'probe' doesn't succeed until the control queue has at least one buffer allocated for its ivq. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 0057bae2036f..c40703759e26 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1057,25 +1057,30 @@ static void config_intr(struct virtio_device *vdev) resize_console(find_port_by_id(portdev, 0)); } -static void fill_queue(struct virtqueue *vq, spinlock_t *lock) +static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) { struct port_buffer *buf; - int ret; + unsigned int ret; + int err; + ret = 0; do { buf = alloc_buf(PAGE_SIZE); if (!buf) break; spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { + err = add_inbuf(vq, buf); + if (err < 0) { spin_unlock_irq(lock); free_buf(buf); break; } + ret++; spin_unlock_irq(lock); - } while (ret > 0); + } while (err > 0); + + return ret; } static int add_port(struct ports_device *portdev, u32 id) @@ -1430,7 +1435,13 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) INIT_WORK(&portdev->control_work, &control_work_handler); INIT_WORK(&portdev->config_work, &config_work_handler); - fill_queue(portdev->c_ivq, &portdev->cvq_lock); + err = fill_queue(portdev->c_ivq, &portdev->cvq_lock); + if (!err) { + dev_err(&vdev->dev, + "Error allocating buffers for control queue\n"); + err = -ENOMEM; + goto free_vqs; + } } for (i = 0; i < portdev->config.nr_ports; i++) @@ -1440,6 +1451,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) early_put_chars = NULL; return 0; +free_vqs: + vdev->config->del_vqs(vdev); + kfree(portdev->in_vqs); + kfree(portdev->out_vqs); free_chrdev: unregister_chrdev(portdev->chr_major, "virtio-portsdev"); free: -- cgit v1.2.3 From d6933561924d8022f5d986ce7c511a2646eeadce Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Fri, 12 Feb 2010 10:32:18 +0530 Subject: virtio: console: Fill ports' entire in_vq with buffers Instead of allocating just one buffer for a port's in_vq, fill the entire in_vq with buffers so the host need not stall while an application consumes the data and makes the buffer available again for the host. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 53 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 21 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c40703759e26..213373b5f17f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -330,6 +330,7 @@ static void discard_port_data(struct port *port) struct port_buffer *buf; struct virtqueue *vq; unsigned int len; + int ret; vq = port->in_vq; if (port->inbuf) @@ -337,16 +338,18 @@ static void discard_port_data(struct port *port) else buf = vq->vq_ops->get_buf(vq, &len); - if (!buf) - return; - - if (add_inbuf(vq, buf) < 0) { - buf->len = buf->offset = 0; - dev_warn(port->dev, "Error adding buffer back to vq\n"); - return; + ret = 0; + while (buf) { + if (add_inbuf(vq, buf) < 0) { + ret++; + free_buf(buf); + } + buf = vq->vq_ops->get_buf(vq, &len); } - port->inbuf = NULL; + if (ret) + dev_warn(port->dev, "Errors adding %d buffers back to vq\n", + ret); } static bool port_has_data(struct port *port) @@ -354,12 +357,19 @@ static bool port_has_data(struct port *port) unsigned long flags; bool ret; - ret = false; spin_lock_irqsave(&port->inbuf_lock, flags); - if (port->inbuf) + if (port->inbuf) { ret = true; + goto out; + } + port->inbuf = get_inbuf(port); + if (port->inbuf) { + ret = true; + goto out; + } + ret = false; +out: spin_unlock_irqrestore(&port->inbuf_lock, flags); - return ret; } @@ -1011,7 +1021,8 @@ static void in_intr(struct virtqueue *vq) return; spin_lock_irqsave(&port->inbuf_lock, flags); - port->inbuf = get_inbuf(port); + if (!port->inbuf) + port->inbuf = get_inbuf(port); /* * Don't queue up data when port is closed. This condition @@ -1087,7 +1098,7 @@ static int add_port(struct ports_device *portdev, u32 id) { char debugfs_name[16]; struct port *port; - struct port_buffer *inbuf; + struct port_buffer *buf; dev_t devt; int err; @@ -1132,22 +1143,21 @@ static int add_port(struct ports_device *portdev, u32 id) spin_lock_init(&port->inbuf_lock); init_waitqueue_head(&port->waitqueue); - inbuf = alloc_buf(PAGE_SIZE); - if (!inbuf) { + /* Fill the in_vq with buffers so the host can send us data. */ + err = fill_queue(port->in_vq, &port->inbuf_lock); + if (!err) { + dev_err(port->dev, "Error allocating inbufs\n"); err = -ENOMEM; goto free_device; } - /* Register the input buffer the first time. */ - add_inbuf(port->in_vq, inbuf); - /* * If we're not using multiport support, this has to be a console port */ if (!use_multiport(port->portdev)) { err = init_port_console(port); if (err) - goto free_inbuf; + goto free_inbufs; } spin_lock_irq(&portdev->ports_lock); @@ -1175,8 +1185,9 @@ static int add_port(struct ports_device *portdev, u32 id) } return 0; -free_inbuf: - free_buf(inbuf); +free_inbufs: + while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) + free_buf(buf); free_device: device_destroy(pdrvdata.class, port->dev->devt); free_cdev: -- cgit v1.2.3 From eb054e3b3d531e311e9a34b902fe29247b00f724 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 20 Aug 2009 22:42:31 +0200 Subject: m68k: vme_scc - __init annotations Trivial patch which adds the __init macro to the module_init function and all of its helper functions of drivers/char/vme_scc.c Signed-off-by: Peter Huewe Signed-off-by: Geert Uytterhoeven --- drivers/char/vme_scc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index 994e1a58b987..8b24729fec89 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -136,7 +136,7 @@ static const struct tty_port_operations scc_port_ops = { * vme_scc_init() and support functions *---------------------------------------------------------------------------*/ -static int scc_init_drivers(void) +static int __init scc_init_drivers(void) { int error; @@ -172,7 +172,7 @@ static int scc_init_drivers(void) /* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1). */ -static void scc_init_portstructs(void) +static void __init scc_init_portstructs(void) { struct scc_port *port; int i; @@ -195,7 +195,7 @@ static void scc_init_portstructs(void) #ifdef CONFIG_MVME147_SCC -static int mvme147_scc_init(void) +static int __init mvme147_scc_init(void) { struct scc_port *port; int error; @@ -298,7 +298,7 @@ fail: #ifdef CONFIG_MVME162_SCC -static int mvme162_scc_init(void) +static int __init mvme162_scc_init(void) { struct scc_port *port; int error; @@ -404,7 +404,7 @@ fail: #ifdef CONFIG_BVME6000_SCC -static int bvme6000_scc_init(void) +static int __init bvme6000_scc_init(void) { struct scc_port *port; int error; @@ -503,7 +503,7 @@ fail_free_b_rx: #endif -static int vme_scc_init(void) +static int __init vme_scc_init(void) { int res = -ENODEV; -- cgit v1.2.3