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 99b089c3c38a83ebaeb1cc4584ddcde841626467 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 30 Jan 2010 00:53:29 -0800 Subject: Input: Mac button emulation - implement as an input filter Current implementation of Mac mouse button emulation plugs into legacy keyboard driver, converts certain keys into button events on a separate device, and suppresses the real events from reaching tty. This worked well enough until user space started using evdev which was completely unaware of this arrangement and kept sending original key presses to its users. Change the implementation to use newly added input filter framework so that original key presses are not transmitted to any handlers. As a bonus remove SYSCTL dependencies from the code and use Kconfig instead; also do not create the emulated mouse device until user activates emulation. Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index f706b1dffdb3..cbf64b985ef4 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1185,11 +1185,6 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) rep = (down == 2); -#ifdef CONFIG_MAC_EMUMOUSEBTN - if (mac_hid_mouse_emulate_buttons(1, keycode, down)) - return; -#endif /* CONFIG_MAC_EMUMOUSEBTN */ - if ((raw_mode = (kbd->kbdmode == VC_RAW)) && !hw_raw) if (emulate_raw(vc, keycode, !down << 7)) if (keycode < BTN_MISC && printk_ratelimit()) -- 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 0b7024ac4df5821347141c18e680b7166bc1cb20 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 2 Feb 2010 21:08:26 -0800 Subject: Input: add match() method to input hanlders Get rid of blacklist in input handler structure and instead allow handlers to define their own match() method to perform fine-grained filtering of supported devices. Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index cbf64b985ef4..ada25bb8941e 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1323,6 +1323,21 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type, schedule_console_callback(); } +static bool kbd_match(struct input_handler *handler, struct input_dev *dev) +{ + int i; + + if (test_bit(EV_SND, dev->evbit)) + return true; + + if (test_bit(EV_KEY, dev->evbit)) + for (i = KEY_RESERVED; i < BTN_MISC; i++) + if (test_bit(i, dev->keybit)) + return true; + + return false; +} + /* * When a keyboard (or other input device) is found, the kbd_connect * function is called. The function then looks at the device, and if it @@ -1334,14 +1349,6 @@ static int kbd_connect(struct input_handler *handler, struct input_dev *dev, { struct input_handle *handle; int error; - int i; - - for (i = KEY_RESERVED; i < BTN_MISC; i++) - if (test_bit(i, dev->keybit)) - break; - - if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit)) - return -ENODEV; handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!handle) @@ -1407,6 +1414,7 @@ MODULE_DEVICE_TABLE(input, kbd_ids); static struct input_handler kbd_handler = { .event = kbd_event, + .match = kbd_match, .connect = kbd_connect, .disconnect = kbd_disconnect, .start = kbd_start, -- 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 --- drivers/char/hvc_beat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 0afc8b82212e..9b397c5ee1d7 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -99,7 +99,7 @@ static int hvc_beat_config(char *p) static int __init hvc_beat_console_init(void) { - if (hvc_beat_useit && machine_is_compatible("Beat")) { + if (hvc_beat_useit && of_machine_is_compatible("Beat")) { hvc_instantiate(0, 0, &hvc_beat_get_put_ops); } return 0; -- cgit v1.2.3 From 088ef950dc0dd58d2f339e1616c9092fea923f06 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 12 Feb 2010 12:26:47 -0800 Subject: omap2: Convert ARCH_OMAP24XX to ARCH_OMAP2 Convert ARCH_OMAP24XX to ARCH_OMAP2 Signed-off-by: Tony Lindgren --- drivers/char/hw_random/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 87060266ef91..c0df30faa66a 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -114,7 +114,7 @@ config HW_RANDOM_IXP4XX config HW_RANDOM_OMAP tristate "OMAP Random Number Generator support" - depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP24XX) + depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP2) default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number -- 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 af86d4b0f064413d2f353a41cdc23b7dbff0823d Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Wed, 10 Feb 2010 10:39:33 +0800 Subject: agp/intel: official names for Pineview and Ironlake Print official names for Pineview and Ironlake, which is Intel GMA3150 and Intel HD graphics. Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 3999a5f25f38..e5ffefee34a0 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2345,9 +2345,9 @@ static const struct intel_driver_description { NULL, &intel_g33_driver }, { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "Pineview", + { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "Pineview", + { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "GMA3150", NULL, &intel_g33_driver }, { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0, "GM45", NULL, &intel_i965_driver }, @@ -2362,13 +2362,13 @@ static const struct intel_driver_description { { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, "G41", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0, - "Ironlake/D", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/M", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/MA", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, - "Ironlake/MC2", NULL, &intel_i965_driver }, + "HD Graphics", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; -- 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 +++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 54 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); -- 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 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 336 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 = { -- 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 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 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) { -- 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 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 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; -- 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 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 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) { -- 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 1089e3009572e07a1fe12c3408828f27c96453be Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 22 Oct 2009 16:10:52 -0700 Subject: agp/intel: Add support for Sandybridge. Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index e5ffefee34a0..df85ed9c7531 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -64,6 +64,8 @@ #define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 /* cover 915 and 945 variants */ #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ @@ -98,7 +100,8 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) extern int agp_memory_reserved; @@ -705,6 +708,12 @@ static void intel_i830_init_gtt_entries(void) gtt_entries = 0; break; } + } else if (agp_bridge->dev->device == + PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { + /* XXX: This is what my A1 silicon has. What's the right + * answer? + */ + gtt_entries = MB(64) - KB(size); } else { switch (gmch_ctrl & I855_GMCH_GMS_MASK) { case I855_GMCH_GMS_STOLEN_1M: @@ -1364,6 +1373,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: *gtt_offset = *gtt_size = MB(2); break; default: @@ -2369,6 +2379,8 @@ static const struct intel_driver_description { "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, "HD Graphics", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, 0, + "Sandybridge", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; @@ -2575,6 +2587,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB), + ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB), { } }; -- cgit v1.2.3 From e3deb204c69c485c88c990f07b71be10a464e508 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 2 Nov 2009 15:33:05 -0800 Subject: agp/intel: Use a non-reserved value for the cache field of the PTEs. I don't know if this is what we'll want to be using long term, we'll see. Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index df85ed9c7531..c3c870bf5678 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -296,6 +296,11 @@ static void intel_agp_insert_sg_entries(struct agp_memory *mem, off_t pg_start, int mask_type) { int i, j; + u32 cache_bits = 0; + + if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { + cache_bits = I830_PTE_SYSTEM_CACHED; + } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, -- cgit v1.2.3 From 14bc490bbdf1b194ad1f5f3d2a0a27edfdf78986 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Wed, 11 Nov 2009 01:25:25 +0800 Subject: drm/i915, agp/intel: Fix stolen memory size on Sandybridge New memory control config reg at 0x50 should be used for stolen memory size detection on Sandybridge. Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 78 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index c3c870bf5678..9a551bc34c39 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -150,6 +150,25 @@ extern int agp_memory_reserved; #define INTEL_I7505_AGPCTRL 0x70 #define INTEL_I7505_MCHCFG 0x50 +#define SNB_GMCH_CTRL 0x50 +#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 +#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) +#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) +#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) +#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) +#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) +#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) +#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) +#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) +#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) +#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) +#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) +#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) +#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) +#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) +#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) +#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) + static const struct aper_size_info_fixed intel_i810_sizes[] = { {64, 16384, 4}, @@ -621,7 +640,7 @@ static struct aper_size_info_fixed intel_i830_sizes[] = static void intel_i830_init_gtt_entries(void) { u16 gmch_ctrl; - int gtt_entries; + int gtt_entries = 0; u8 rdct; int local = 0; static const int ddt[4] = { 0, 16, 32, 64 }; @@ -715,10 +734,61 @@ static void intel_i830_init_gtt_entries(void) } } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { - /* XXX: This is what my A1 silicon has. What's the right - * answer? + /* + * SandyBridge has new memory control reg at 0x50.w */ - gtt_entries = MB(64) - KB(size); + u16 snb_gmch_ctl; + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { + case SNB_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_64M: + gtt_entries = MB(64) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_96M: + gtt_entries = MB(96) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_128M: + gtt_entries = MB(128) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_160M: + gtt_entries = MB(160) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_192M: + gtt_entries = MB(192) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_224M: + gtt_entries = MB(224) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_256M: + gtt_entries = MB(256) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_288M: + gtt_entries = MB(288) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_320M: + gtt_entries = MB(320) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_352M: + gtt_entries = MB(352) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_384M: + gtt_entries = MB(384) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_416M: + gtt_entries = MB(416) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_448M: + gtt_entries = MB(448) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_480M: + gtt_entries = MB(480) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_512M: + gtt_entries = MB(512) - KB(size); + break; + } } else { switch (gmch_ctrl & I855_GMCH_GMS_MASK) { case I855_GMCH_GMS_STOLEN_1M: -- cgit v1.2.3 From 954bce507acdcb76520b2f30535400fc036e7c20 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 7 Jan 2010 16:21:46 -0800 Subject: agp/intel: Add a new Sandybridge HB/IG PCI ID combo. Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 9a551bc34c39..918e484f41b7 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -66,6 +66,8 @@ #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 #define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 #define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG 0x0106 /* cover 915 and 945 variants */ #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ @@ -101,7 +103,8 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) extern int agp_memory_reserved; @@ -317,7 +320,9 @@ static void intel_agp_insert_sg_entries(struct agp_memory *mem, int i, j; u32 cache_bits = 0; - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { + if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) + { cache_bits = I830_PTE_SYSTEM_CACHED; } @@ -732,8 +737,8 @@ static void intel_i830_init_gtt_entries(void) gtt_entries = 0; break; } - } else if (agp_bridge->dev->device == - PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { + } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) { /* * SandyBridge has new memory control reg at 0x50.w */ @@ -1449,6 +1454,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: *gtt_offset = *gtt_size = MB(2); break; default: @@ -2456,6 +2462,8 @@ static const struct intel_driver_description { "HD Graphics", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, 0, "Sandybridge", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, 0, + "Sandybridge", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; @@ -2663,6 +2671,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB), ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB), + ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB), { } }; -- cgit v1.2.3 From 1f7a6e372e9cb4d749f34c0738d832e6cadb4071 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Tue, 23 Feb 2010 14:05:24 +0800 Subject: drm/i915: Add dependency on the intel agp module See http://bugzilla.kernel.org/show_bug.cgi?id=15021 Make sure that the appropriate AGP module is loaded and probed before trying to set up the DRM. The DRM already depends on the AGP core, but in this case we know the specific AGP driver we need too, and can help users avoid the trap of loading the AGP driver after the DRM driver. Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 918e484f41b7..c1c07a2ccb11 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -10,6 +10,9 @@ #include #include "agp.h" +int intel_agp_enabled; +EXPORT_SYMBOL(intel_agp_enabled); + /* * If we have Intel graphics, we're not going to have anything other than * an Intel IOMMU. So make the correct use of the PCI DMA API contingent @@ -2473,7 +2476,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, struct agp_bridge_data *bridge; u8 cap_ptr = 0; struct resource *r; - int i; + int i, err; cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); @@ -2565,7 +2568,10 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, } pci_set_drvdata(pdev, bridge); - return agp_add_bridge(bridge); + err = agp_add_bridge(bridge); + if (!err) + intel_agp_enabled = 1; + return err; } static void __devexit agp_intel_remove(struct pci_dev *pdev) -- 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 From de538eb3441e8b9f7aedb3a37e46c005538451dc Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Wed, 9 Dec 2009 12:31:39 -0800 Subject: serial: synclink_gt: dropped transmit data bugfix Fix transmit bug that could drop send data if write() called close to serial transmitter going idle after sending previous data. Bug is caused by incorrect use of device information member tx_count. Driver originally processed one data block (write call) at a time, waiting for transmit idle before sending more. tx_count recorded how much data was loaded in DMA buffers on write(), and was cleared on send completion. tx_count use was overloaded to record accumulated data from put_char() callback when transmitter was idle. A bug was introduced when transmit code was reworked to allow multiple blocks of data in the tx DMA buffers which keeps transmitter from going idle between blocks. tx_count was set to size of last block loaded, cleared when tx went idle, and monitored to know when to restart transmitter without proper synchronization. tx_count could be cleared when unsent data remained in DMA buffers and transmitter required restarting, effectively dropping unsent data. Solution: 1. tx_count now used only to track accumulated data from put_char 2. DMA buffer state tracked by direct inspection of descriptors with spinlock synchronization 3. consolidate these tasks in tx_load() : a. check for available buffer space b. load buffers c. restart DMA and or serial transmitter as needed These steps were previously duplicated in multiple places, sometimes incompletely. 4. fix use of tx_count as active transmit indicator, instead using tx_active which is meant for that purpose Signed-off-by: Paul Fulghum Cc: Alan Cox Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/synclink_gt.c | 186 +++++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 81 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 8678f0c8699d..4561ce2fba6d 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -468,7 +468,7 @@ static unsigned int free_tbuf_count(struct slgt_info *info); static unsigned int tbuf_bytes(struct slgt_info *info); static void reset_tbufs(struct slgt_info *info); static void tdma_reset(struct slgt_info *info); -static void tx_load(struct slgt_info *info, const char *buf, unsigned int count); +static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count); static void get_signals(struct slgt_info *info); static void set_signals(struct slgt_info *info); @@ -813,59 +813,32 @@ static int write(struct tty_struct *tty, int ret = 0; struct slgt_info *info = tty->driver_data; unsigned long flags; - unsigned int bufs_needed; if (sanity_check(info, tty->name, "write")) - goto cleanup; + return -EIO; + DBGINFO(("%s write count=%d\n", info->device_name, count)); - if (!info->tx_buf) - goto cleanup; + if (!info->tx_buf || (count > info->max_frame_size)) + return -EIO; - if (count > info->max_frame_size) { - ret = -EIO; - goto cleanup; - } + if (!count || tty->stopped || tty->hw_stopped) + return 0; - if (!count) - goto cleanup; + spin_lock_irqsave(&info->lock, flags); - if (!info->tx_active && info->tx_count) { + if (info->tx_count) { /* send accumulated data from send_char() */ - tx_load(info, info->tx_buf, info->tx_count); - goto start; + if (!tx_load(info, info->tx_buf, info->tx_count)) + goto cleanup; + info->tx_count = 0; } - bufs_needed = (count/DMABUFSIZE); - if (count % DMABUFSIZE) - ++bufs_needed; - if (bufs_needed > free_tbuf_count(info)) - goto cleanup; - ret = info->tx_count = count; - tx_load(info, buf, count); - goto start; - -start: - if (info->tx_count && !tty->stopped && !tty->hw_stopped) { - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active) - tx_start(info); - else if (!(rd_reg32(info, TDCSR) & BIT0)) { - /* transmit still active but transmit DMA stopped */ - unsigned int i = info->tbuf_current; - if (!i) - i = info->tbuf_count; - i--; - /* if DMA buf unsent must try later after tx idle */ - if (desc_count(info->tbufs[i])) - ret = 0; - } - if (ret > 0) - update_tx_timer(info); - spin_unlock_irqrestore(&info->lock,flags); - } + if (tx_load(info, buf, count)) + ret = count; cleanup: + spin_unlock_irqrestore(&info->lock, flags); DBGINFO(("%s write rc=%d\n", info->device_name, ret)); return ret; } @@ -882,7 +855,7 @@ static int put_char(struct tty_struct *tty, unsigned char ch) if (!info->tx_buf) return 0; spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active && (info->tx_count < info->max_frame_size)) { + if (info->tx_count < info->max_frame_size) { info->tx_buf[info->tx_count++] = ch; ret = 1; } @@ -981,10 +954,8 @@ static void flush_chars(struct tty_struct *tty) DBGINFO(("%s flush_chars start transmit\n", info->device_name)); spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active && info->tx_count) { - tx_load(info, info->tx_buf,info->tx_count); - tx_start(info); - } + if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) + info->tx_count = 0; spin_unlock_irqrestore(&info->lock,flags); } @@ -997,10 +968,9 @@ static void flush_buffer(struct tty_struct *tty) return; DBGINFO(("%s flush_buffer\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active) - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock,flags); + spin_lock_irqsave(&info->lock, flags); + info->tx_count = 0; + spin_unlock_irqrestore(&info->lock, flags); tty_wakeup(tty); } @@ -1033,12 +1003,10 @@ static void tx_release(struct tty_struct *tty) if (sanity_check(info, tty->name, "tx_release")) return; DBGINFO(("%s tx_release\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active && info->tx_count) { - tx_load(info, info->tx_buf, info->tx_count); - tx_start(info); - } - spin_unlock_irqrestore(&info->lock,flags); + spin_lock_irqsave(&info->lock, flags); + if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) + info->tx_count = 0; + spin_unlock_irqrestore(&info->lock, flags); } /* @@ -1506,27 +1474,25 @@ static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, DBGINFO(("%s hdlc_xmit\n", dev->name)); + if (!skb->len) + return NETDEV_TX_OK; + /* stop sending until this frame completes */ netif_stop_queue(dev); - /* copy data to device buffers */ - info->tx_count = skb->len; - tx_load(info, skb->data, skb->len); - /* update network statistics */ dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - /* save start time for transmit timeout detection */ dev->trans_start = jiffies; - spin_lock_irqsave(&info->lock,flags); - tx_start(info); - update_tx_timer(info); - spin_unlock_irqrestore(&info->lock,flags); + spin_lock_irqsave(&info->lock, flags); + tx_load(info, skb->data, skb->len); + spin_unlock_irqrestore(&info->lock, flags); + + /* done with socket buffer, so free it */ + dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -2180,7 +2146,7 @@ static void isr_serial(struct slgt_info *info) if (info->params.mode == MGSL_MODE_ASYNC) { if (status & IRQ_TXIDLE) { - if (info->tx_count) + if (info->tx_active) isr_txeom(info, status); } if (info->rx_pio && (status & IRQ_RXDATA)) @@ -2276,13 +2242,42 @@ static void isr_tdma(struct slgt_info *info) } } +/* + * return true if there are unsent tx DMA buffers, otherwise false + * + * if there are unsent buffers then info->tbuf_start + * is set to index of first unsent buffer + */ +static bool unsent_tbufs(struct slgt_info *info) +{ + unsigned int i = info->tbuf_current; + bool rc = false; + + /* + * search backwards from last loaded buffer (precedes tbuf_current) + * for first unsent buffer (desc_count > 0) + */ + + do { + if (i) + i--; + else + i = info->tbuf_count - 1; + if (!desc_count(info->tbufs[i])) + break; + info->tbuf_start = i; + rc = true; + } while (i != info->tbuf_current); + + return rc; +} + static void isr_txeom(struct slgt_info *info, unsigned short status) { DBGISR(("%s txeom status=%04x\n", info->device_name, status)); slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); tdma_reset(info); - reset_tbufs(info); if (status & IRQ_TXUNDER) { unsigned short val = rd_reg16(info, TCR); wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ @@ -2297,8 +2292,12 @@ static void isr_txeom(struct slgt_info *info, unsigned short status) info->icount.txok++; } + if (unsent_tbufs(info)) { + tx_start(info); + update_tx_timer(info); + return; + } info->tx_active = false; - info->tx_count = 0; del_timer(&info->tx_timer); @@ -3949,7 +3948,7 @@ static void tx_start(struct slgt_info *info) info->tx_enabled = true; } - if (info->tx_count) { + if (desc_count(info->tbufs[info->tbuf_start])) { info->drop_rts_on_tx_done = false; if (info->params.mode != MGSL_MODE_ASYNC) { @@ -4772,25 +4771,36 @@ static unsigned int tbuf_bytes(struct slgt_info *info) } /* - * load transmit DMA buffer(s) with data + * load data into transmit DMA buffer ring and start transmitter if needed + * return true if data accepted, otherwise false (buffers full) */ -static void tx_load(struct slgt_info *info, const char *buf, unsigned int size) +static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size) { unsigned short count; unsigned int i; struct slgt_desc *d; - if (size == 0) - return; + /* check required buffer space */ + if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) + return false; DBGDATA(info, buf, size, "tx"); + /* + * copy data to one or more DMA buffers in circular ring + * tbuf_start = first buffer for this data + * tbuf_current = next free buffer + * + * Copy all data before making data visible to DMA controller by + * setting descriptor count of the first buffer. + * This prevents an active DMA controller from reading the first DMA + * buffers of a frame and stopping before the final buffers are filled. + */ + info->tbuf_start = i = info->tbuf_current; while (size) { d = &info->tbufs[i]; - if (++i == info->tbuf_count) - i = 0; count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); memcpy(d->buf, buf, count); @@ -4808,11 +4818,27 @@ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size) else set_desc_eof(*d, 0); - set_desc_count(*d, count); + /* set descriptor count for all but first buffer */ + if (i != info->tbuf_start) + set_desc_count(*d, count); d->buf_count = count; + + if (++i == info->tbuf_count) + i = 0; } info->tbuf_current = i; + + /* set first buffer count to make new data visible to DMA controller */ + d = &info->tbufs[info->tbuf_start]; + set_desc_count(*d, d->buf_count); + + /* start transmitter if needed and update transmit timeout */ + if (!info->tx_active) + tx_start(info); + update_tx_timer(info); + + return true; } static int register_test(struct slgt_info *info) @@ -4934,9 +4960,7 @@ static int loopback_test(struct slgt_info *info) spin_lock_irqsave(&info->lock,flags); async_mode(info); rx_start(info); - info->tx_count = count; tx_load(info, buf, count); - tx_start(info); spin_unlock_irqrestore(&info->lock, flags); /* wait for receive complete */ -- cgit v1.2.3 From a357e777b5982d5f460a2783d697a8a90d497348 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Dec 2009 12:34:13 -0800 Subject: serial: Char: cyclades, fix compiler warning With gcc 4.0.2: drivers/char/cyclades.c: In function 'cyy_interrupt': drivers/char/cyclades.c:581: warning: 'info' may be used uninitialized in this function introduced by : commit 3aeea5b92210083c7cffd4f08a0bb141d3f2d574 : Author: Jiri Slaby : AuthorDate: Sat Sep 19 13:13:16 2009 -0700 : Commit: Live-CD User : CommitDate: Sat Sep 19 13:13:16 2009 -0700 : : cyclades: introduce cyy_readb/writeb In fact the true branch which uses uninitialized 'info' can never happen because chip is always less than ->nchips and channel is always less than 4 which we alloc. So behave similar to rx handling and remove the test completely. I wonder why gcc 4.4.1 doesn't spit a word. Reported-by: Andrew Morton Cc: Alan Cox Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 4254457d3911..274e9651fa36 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -598,12 +598,6 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); - /* validate the port# (as configured and open) */ - if (channel + chip * 4 >= cinfo->nports) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyTxRdy); - goto end; - } info = &cinfo->ports[channel + chip * 4]; tty = tty_port_tty_get(&info->port); if (tty == NULL) { -- cgit v1.2.3 From 2090436357c20afad377a61c789f502c36d637de Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 9 Dec 2009 12:34:14 -0800 Subject: serial: cyclades: allow overriding ISA defaults also when the driver is built-in Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 274e9651fa36..e026f2447923 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -158,13 +158,11 @@ static unsigned int cy_isa_addresses[] = { #define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) -#ifdef MODULE static long maddr[NR_CARDS]; static int irq[NR_CARDS]; module_param_array(maddr, long, NULL, 0); module_param_array(irq, int, NULL, 0); -#endif #endif /* CONFIG_ISA */ @@ -3310,13 +3308,10 @@ static int __init cy_detect_isa(void) unsigned short cy_isa_irq, nboard; void __iomem *cy_isa_address; unsigned short i, j, cy_isa_nchan; -#ifdef MODULE int isparam = 0; -#endif nboard = 0; -#ifdef MODULE /* Check for module parameters */ for (i = 0; i < NR_CARDS; i++) { if (maddr[i] || i) { @@ -3326,7 +3321,6 @@ static int __init cy_detect_isa(void) if (!maddr[i]) break; } -#endif /* scan the address table probing for Cyclom-Y/ISA boards */ for (i = 0; i < NR_ISA_ADDRS; i++) { @@ -3347,11 +3341,10 @@ static int __init cy_detect_isa(void) iounmap(cy_isa_address); continue; } -#ifdef MODULE + if (isparam && i < NR_CARDS && irq[i]) cy_isa_irq = irq[i]; else -#endif /* find out the board's irq by probing */ cy_isa_irq = detect_isa_irq(cy_isa_address); if (cy_isa_irq == 0) { -- cgit v1.2.3 From f43a510de8af5748637de21c96f82232b24920e4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Dec 2009 12:34:15 -0800 Subject: tty: char: mxser, remove unnecessary tty test Stanse found unnecessary test in mxser_startup. tty is dereferenced earlier, the test is superfluous. Remove it. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/mxser.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 3d923065d9a2..e0c5d2a69046 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -895,8 +895,7 @@ static int mxser_activate(struct tty_port *port, struct tty_struct *tty) if (inb(info->ioaddr + UART_LSR) == 0xff) { spin_unlock_irqrestore(&info->slock, flags); if (capable(CAP_SYS_ADMIN)) { - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); return 0; } else return -ENODEV; -- cgit v1.2.3 From db91340b2e874693f14e9e937152e7433d7ac753 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 9 Dec 2009 12:34:16 -0800 Subject: serial: isicom.c: use pr_fmt and pr_ Add #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt Convert printks to pr_ Convert some embedded function names to %s...__func__ Remove a period after exclamation points. Remove #define pr_dbg which could be used by future kernel.h includes Signed-off-by: Joe Perches Acked-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/isicom.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 300d5bd6cd06..77d5a29ce80e 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -113,6 +113,8 @@ * 64-bit verification */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -140,7 +142,6 @@ #define InterruptTheCard(base) outw(0, (base) + 0xc) #define ClearInterrupt(base) inw((base) + 0x0a) -#define pr_dbg(str...) pr_debug("ISICOM: " str) #ifdef DEBUG #define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c)) #else @@ -249,8 +250,7 @@ static int lock_card(struct isi_board *card) spin_unlock_irqrestore(&card->card_lock, card->flags); msleep(10); } - printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%lx)\n", - card->base); + pr_warning("Failed to lock Card (0x%lx)\n", card->base); return 0; /* Failed to acquire the card! */ } @@ -379,13 +379,13 @@ static inline int __isicom_paranoia_check(struct isi_port const *port, char *name, const char *routine) { if (!port) { - printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for " - "dev %s in %s.\n", name, routine); + pr_warning("Warning: bad isicom magic for dev %s in %s.\n", + name, routine); return 1; } if (port->magic != ISICOM_MAGIC) { - printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for " - "dev %s in %s.\n", name, routine); + pr_warning("Warning: NULL isicom port for dev %s in %s.\n", + name, routine); return 1; } @@ -450,8 +450,8 @@ static void isicom_tx(unsigned long _data) if (!(inw(base + 0x02) & (1 << port->channel))) continue; - pr_dbg("txing %d bytes, port%d.\n", txcount, - port->channel + 1); + pr_debug("txing %d bytes, port%d.\n", + txcount, port->channel + 1); outw((port->channel << isi_card[card].shift_count) | txcount, base); residue = NO; @@ -547,8 +547,8 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) byte_count = header & 0xff; if (channel + 1 > card->port_count) { - printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): " - "%d(channel) > port_count.\n", base, channel+1); + pr_warning("%s(0x%lx): %d(channel) > port_count.\n", + __func__, base, channel+1); outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; @@ -582,14 +582,15 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) if (port->status & ISI_DCD) { if (!(header & ISI_DCD)) { /* Carrier has been lost */ - pr_dbg("interrupt: DCD->low.\n" - ); + pr_debug("%s: DCD->low.\n", + __func__); port->status &= ~ISI_DCD; tty_hangup(tty); } } else if (header & ISI_DCD) { /* Carrier has been detected */ - pr_dbg("interrupt: DCD->high.\n"); + pr_debug("%s: DCD->high.\n", + __func__); port->status |= ISI_DCD; wake_up_interruptible(&port->port.open_wait); } @@ -641,17 +642,19 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) break; case 2: /* Statistics */ - pr_dbg("isicom_interrupt: stats!!!.\n"); + pr_debug("%s: stats!!!\n", __func__); break; default: - pr_dbg("Intr: Unknown code in status packet.\n"); + pr_debug("%s: Unknown code in status packet.\n", + __func__); break; } } else { /* Data Packet */ count = tty_prepare_flip_string(tty, &rp, byte_count & ~1); - pr_dbg("Intr: Can rx %d of %d bytes.\n", count, byte_count); + pr_debug("%s: Can rx %d of %d bytes.\n", + __func__, count, byte_count); word_count = count >> 1; insw(base, rp, word_count); byte_count -= (word_count << 1); @@ -661,8 +664,8 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) byte_count -= 2; } if (byte_count > 0) { - pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping " - "bytes...\n", base, channel + 1); + pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n", + __func__, base, channel + 1); /* drain out unread xtra data */ while (byte_count > 0) { inw(base); @@ -888,8 +891,8 @@ static void isicom_shutdown_port(struct isi_port *port) struct isi_board *card = port->card; if (--card->count < 0) { - pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n", - card->base, card->count); + pr_debug("%s: bad board(0x%lx) count %d.\n", + __func__, card->base, card->count); card->count = 0; } /* last port was closed, shutdown that board too */ @@ -1681,13 +1684,13 @@ static int __init isicom_init(void) retval = tty_register_driver(isicom_normal); if (retval) { - pr_dbg("Couldn't register the dialin driver\n"); + pr_debug("Couldn't register the dialin driver\n"); goto err_puttty; } retval = pci_register_driver(&isicom_driver); if (retval < 0) { - printk(KERN_ERR "ISICOM: Unable to register pci driver.\n"); + pr_err("Unable to register pci driver.\n"); goto err_unrtty; } -- cgit v1.2.3 From ecfcbee729389bc47576695c529d351d60946ca7 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 9 Dec 2009 12:34:16 -0800 Subject: hvc_console: fix test on unsigned in hvc_console_print() vtermnos[] is unsigned, so this test was wrong. Signed-off-by: Roel Kluin Cc: Benjamin Herrenschmidt Cc: Hendrik Brueckner Cc: Christian Borntraeger Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/hvc_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 4c3b59be286a..465185fc0f52 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -146,7 +146,7 @@ static void hvc_console_print(struct console *co, const char *b, return; /* This console adapter was removed so it is not usable. */ - if (vtermnos[index] < 0) + if (vtermnos[index] == -1) return; while (count > 0 || i > 0) { -- cgit v1.2.3 From c6fc826e4c51d2c54913c2a6d800159a2c7dac4b Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Wed, 9 Dec 2009 12:34:18 -0800 Subject: tty: moxa: remove #ifdef MODULE completely. We can pass "module parameters" on the kernel command line even when !MODULE. So, #ifdef MODULE becomes obsolete. Also move the declaration moxa_board_conf at the start of the function, since we were hit by the following warning. drivers/char/moxa.c: In function `moxa_init': drivers/char/moxa.c:1040: warning: ISO C90 forbids mixed declarations and code Signed-off-by: Rakib Mullick Acked-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/moxa.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 63ee3bbc1ce4..49a1fc2ec6f2 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -164,24 +164,22 @@ static unsigned int moxaFuncTout = HZ / 2; static unsigned int moxaLowWaterChk; static DEFINE_MUTEX(moxa_openlock); static DEFINE_SPINLOCK(moxa_lock); -/* Variables for insmod */ -#ifdef MODULE + static unsigned long baseaddr[MAX_BOARDS]; static unsigned int type[MAX_BOARDS]; static unsigned int numports[MAX_BOARDS]; -#endif MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); MODULE_LICENSE("GPL"); -#ifdef MODULE + module_param_array(type, uint, NULL, 0); MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); module_param_array(baseaddr, ulong, NULL, 0); MODULE_PARM_DESC(baseaddr, "base address"); module_param_array(numports, uint, NULL, 0); MODULE_PARM_DESC(numports, "numports (ignored for C218)"); -#endif + module_param(ttymajor, int, 0); /* @@ -1024,6 +1022,8 @@ static int __init moxa_init(void) { unsigned int isabrds = 0; int retval = 0; + struct moxa_board_conf *brd = moxa_boards; + unsigned int i; printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION); @@ -1051,10 +1051,7 @@ static int __init moxa_init(void) } /* Find the boards defined from module args. */ -#ifdef MODULE - { - struct moxa_board_conf *brd = moxa_boards; - unsigned int i; + for (i = 0; i < MAX_BOARDS; i++) { if (!baseaddr[i]) break; @@ -1087,8 +1084,6 @@ static int __init moxa_init(void) isabrds++; } } - } -#endif #ifdef CONFIG_PCI retval = pci_register_driver(&moxa_pci_driver); -- cgit v1.2.3 From 795877cfa4a4db0982ac21e095dad9fbdc909647 Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Wed, 9 Dec 2009 12:34:18 -0800 Subject: ip2: remove #ifdef MODULE from ip2main.c On the kernel command line we can pass "module parameters". So #ifdef MODULE is obsolute now. Remove it completely. When CONFIG_PCI=n and building ip2main.c then we are hit by the following warning. So move *pdev into #ifdef CONFIG_PCI. drivers/char/ip2/ip2main.c: In function `ip2_loadmain': drivers/char/ip2/ip2main.c:542: warning: unused variable `pdev' Signed-off-by: Rakib Mullick Acked-by: Michael H. Warfield Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/ip2/ip2main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 517271c762e6..2913d05e8257 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -263,7 +263,7 @@ static int tracewrap; /* Macros */ /**********/ -#if defined(MODULE) && defined(IP2DEBUG_OPEN) +#ifdef IP2DEBUG_OPEN #define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ tty->name,(pCh->flags), \ tty->count,/*GET_USE_COUNT(module)*/0,s) @@ -487,7 +487,6 @@ static const struct firmware *ip2_request_firmware(void) return fw; } -#ifndef MODULE /****************************************************************************** * ip2_setup: * str: kernel command line string @@ -531,7 +530,6 @@ static int __init ip2_setup(char *str) return 1; } __setup("ip2=", ip2_setup); -#endif /* !MODULE */ static int __init ip2_loadmain(void) { @@ -539,7 +537,6 @@ static int __init ip2_loadmain(void) int err = 0; i2eBordStrPtr pB = NULL; int rc = -1; - struct pci_dev *pdev = NULL; const struct firmware *fw = NULL; if (poll_only) { @@ -612,6 +609,7 @@ static int __init ip2_loadmain(void) case PCI: #ifdef CONFIG_PCI { + struct pci_dev *pdev = NULL; u32 addr; int status; @@ -626,7 +624,7 @@ static int __init ip2_loadmain(void) if (pci_enable_device(pdev)) { dev_err(&pdev->dev, "can't enable device\n"); - break; + goto out; } ip2config.type[i] = PCI; ip2config.pci_dev[i] = pci_dev_get(pdev); @@ -638,6 +636,8 @@ static int __init ip2_loadmain(void) dev_err(&pdev->dev, "I/O address error\n"); ip2config.irq[i] = pdev->irq; +out: + pci_dev_put(pdev); } #else printk(KERN_ERR "IP2: PCI card specified but PCI " @@ -656,7 +656,6 @@ static int __init ip2_loadmain(void) break; } /* switch */ } /* for */ - pci_dev_put(pdev); for (i = 0; i < IP2_MAX_BOARDS; ++i) { if (ip2config.addr[i]) { -- cgit v1.2.3 From 02c95a62d8a0ad0ce71653b9f860b534f89ecc7b Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Sat, 23 Jan 2010 18:53:51 +0600 Subject: ip2: Add module parameter. Stephen Rothwell found the following warning (x86_64 allmodconfig): drivers/char/ip2/ip2main.c:511: warning: 'ip2_setup' defined but not used This patch adds module parameter to fix the above warning. Signed-off-by: Rakib Mullick Signed-off-by: Greg Kroah-Hartman --- drivers/char/ip2/ip2main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 2913d05e8257..c2c915a97667 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -208,6 +208,7 @@ static int DumpFifoBuffer( char __user *, int); static void ip2_init_board(int, const struct firmware *); static unsigned short find_eisa_board(int); +static int ip2_setup(char *str); /***************/ /* Static Data */ @@ -285,7 +286,10 @@ MODULE_AUTHOR("Doug McNash"); MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); MODULE_LICENSE("GPL"); +#define MAX_CMD_STR 50 + static int poll_only; +static char cmd[MAX_CMD_STR]; static int Eisa_irq; static int Eisa_slot; @@ -309,6 +313,8 @@ module_param_array(io, int, NULL, 0); MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards"); module_param(poll_only, bool, 0); MODULE_PARM_DESC(poll_only, "Do not use card interrupts"); +module_param_string(ip2, cmd, MAX_CMD_STR, 0); +MODULE_PARM_DESC(ip2, "Contains module parameter passed with 'ip2='"); /* for sysfs class support */ static struct class *ip2_class; @@ -538,12 +544,19 @@ static int __init ip2_loadmain(void) i2eBordStrPtr pB = NULL; int rc = -1; const struct firmware *fw = NULL; + char *str; + + str = cmd; if (poll_only) { /* Hard lock the interrupts to zero */ irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0; } + /* Check module parameter with 'ip2=' has been passed or not */ + if (!poll_only && (!strncmp(str, "ip2=", 4))) + ip2_setup(str); + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0); /* process command line arguments to modprobe or -- cgit v1.2.3 From e6c4ef984ebbd1a0458503417da91f3de47cbbe0 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 13 Jan 2010 23:34:18 +0000 Subject: tty: declare MODULE_FIRMWARE in various drivers Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 1 + drivers/char/ip2/ip2main.c | 2 ++ drivers/char/isicom.c | 5 +++++ drivers/char/moxa.c | 3 +++ 4 files changed, 11 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e026f2447923..b861c08263a4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -4195,3 +4195,4 @@ module_exit(cy_cleanup_module); MODULE_LICENSE("GPL"); MODULE_VERSION(CY_VERSION); MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); +MODULE_FIRMWARE("cyzfirm.bin"); diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index c2c915a97667..911e1da6def2 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -3209,3 +3209,5 @@ static struct pci_device_id ip2main_pci_tbl[] __devinitdata = { }; MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl); + +MODULE_FIRMWARE("intelliport2.bin"); diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 77d5a29ce80e..be2e8f9a27c3 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1720,3 +1720,8 @@ module_exit(isicom_exit); MODULE_AUTHOR("MultiTech"); MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("isi608.bin"); +MODULE_FIRMWARE("isi608em.bin"); +MODULE_FIRMWARE("isi616em.bin"); +MODULE_FIRMWARE("isi4608.bin"); +MODULE_FIRMWARE("isi4616.bin"); diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 49a1fc2ec6f2..166495d6a1d7 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -172,6 +172,9 @@ static unsigned int numports[MAX_BOARDS]; MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("c218tunx.cod"); +MODULE_FIRMWARE("cp204unx.cod"); +MODULE_FIRMWARE("c320tunx.cod"); module_param_array(type, uint, NULL, 0); MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); -- cgit v1.2.3 From ca1cce49c2778439d00bbf516f70386f3d3b71e5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 10 Jan 2010 12:30:16 +0100 Subject: Char: synclink, remove unnecessary checks Stanse found a potential null dereference in mgsl_put_char and mgsl_write. There is a check for tty being NULL, but it is dereferenced earlier. Actually, tty cannot be NULL in .write and .put_char, so remove the tests. Signed-off-by: Jiri Slaby Cc: Andrew Morton Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/synclink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 4846b73ef28d..0658fc548222 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2031,7 +2031,7 @@ static int mgsl_put_char(struct tty_struct *tty, unsigned char ch) if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char")) return 0; - if (!tty || !info->xmit_buf) + if (!info->xmit_buf) return 0; spin_lock_irqsave(&info->irq_spinlock, flags); @@ -2121,7 +2121,7 @@ static int mgsl_write(struct tty_struct * tty, if (mgsl_paranoia_check(info, tty->name, "mgsl_write")) goto cleanup; - if (!tty || !info->xmit_buf) + if (!info->xmit_buf) goto cleanup; if ( info->params.mode == MGSL_MODE_HDLC || -- cgit v1.2.3 From 266794eb71cfee65b38371954b9db2f6dbda207c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 8 Feb 2010 10:06:45 +0000 Subject: nozomi: Add tty_port usage The Nozomi tty handling is very broken on the open/close side (See http://bugzilla.kernel.org/show_bug.cgi?id=13024 for one example). In particular it marks the tty as closed on the first close() not on the last. Most of the logic is pretty solid except for the open/close path so switch to the tty_port helpers and let them do all the heavy lifting. This is also fixes all the POSIX behaviour violations in the open/close paths. Begin by adding the tty port usage Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/nozomi.c | 115 +++++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 47 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 2ad7d37afbd0..cd405bcf53a6 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -371,6 +371,8 @@ struct port { struct mutex tty_sem; wait_queue_head_t tty_wait; struct async_icount tty_icount; + + struct nozomi *dc; }; /* Private data one for each card in the system */ @@ -414,6 +416,8 @@ MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl); static struct nozomi *ndevs[NOZOMI_MAX_CARDS]; static struct tty_driver *ntty_driver; +static const struct tty_port_operations noz_tty_port_ops; + /* * find card by tty_index */ @@ -1473,9 +1477,11 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev, for (i = 0; i < MAX_PORT; i++) { struct device *tty_dev; - - mutex_init(&dc->port[i].tty_sem); - tty_port_init(&dc->port[i].port); + struct port *port = &dc->port[i]; + port->dc = dc; + mutex_init(&port->tty_sem); + tty_port_init(&port->port); + port->port.ops = &noz_tty_port_ops; tty_dev = tty_register_device(ntty_driver, dc->index_start + i, &pdev->dev); @@ -1600,67 +1606,74 @@ static void set_dtr(const struct tty_struct *tty, int dtr) * ---------------------------------------------------------------------------- */ -/* Called when the userspace process opens the tty, /dev/noz*. */ -static int ntty_open(struct tty_struct *tty, struct file *file) +static int ntty_install(struct tty_driver *driver, struct tty_struct *tty) { struct port *port = get_port_by_tty(tty); struct nozomi *dc = get_dc_by_tty(tty); - unsigned long flags; - + int ret; if (!port || !dc || dc->state != NOZOMI_STATE_READY) return -ENODEV; - - if (mutex_lock_interruptible(&port->tty_sem)) - return -ERESTARTSYS; - - port->port.count++; - dc->open_ttys++; - - /* Enable interrupt downlink for channel */ - if (port->port.count == 1) { - tty->driver_data = port; - tty_port_tty_set(&port->port, tty); - DBG1("open: %d", port->token_dl); - spin_lock_irqsave(&dc->spin_mutex, flags); - dc->last_ier = dc->last_ier | port->token_dl; - writew(dc->last_ier, dc->reg_ier); - spin_unlock_irqrestore(&dc->spin_mutex, flags); + ret = tty_init_termios(tty); + if (ret == 0) { + tty_driver_kref_get(driver); + driver->ttys[tty->index] = tty; } - mutex_unlock(&port->tty_sem); - return 0; + return ret; } -/* Called when the userspace process close the tty, /dev/noz*. Also - called immediately if ntty_open fails in which case tty->driver_data - will be NULL an we exit by the first return */ +static void ntty_cleanup(struct tty_struct *tty) +{ + tty->driver_data = NULL; +} -static void ntty_close(struct tty_struct *tty, struct file *file) +static int ntty_activate(struct tty_port *tport, struct tty_struct *tty) { - struct nozomi *dc = get_dc_by_tty(tty); - struct port *nport = tty->driver_data; - struct tty_port *port = &nport->port; + struct port *port = container_of(tport, struct port, port); + struct nozomi *dc = port->dc; unsigned long flags; - if (!dc || !nport) - return; + DBG1("open: %d", port->token_dl); + spin_lock_irqsave(&dc->spin_mutex, flags); + dc->last_ier = dc->last_ier | port->token_dl; + writew(dc->last_ier, dc->reg_ier); + dc->open_ttys++; + spin_unlock_irqrestore(&dc->spin_mutex, flags); + printk("noz: activated %d: %p\n", tty->index, tport); + return 0; +} - /* Users cannot interrupt a close */ - mutex_lock(&nport->tty_sem); +static int ntty_open(struct tty_struct *tty, struct file *filp) +{ + struct port *port = get_port_by_tty(tty); + return tty_port_open(&port->port, tty, filp); +} - WARN_ON(!port->count); +static void ntty_shutdown(struct tty_port *tport) +{ + struct port *port = container_of(tport, struct port, port); + struct nozomi *dc = port->dc; + unsigned long flags; + DBG1("close: %d", port->token_dl); + spin_lock_irqsave(&dc->spin_mutex, flags); + dc->last_ier &= ~(port->token_dl); + writew(dc->last_ier, dc->reg_ier); dc->open_ttys--; - port->count--; + spin_unlock_irqrestore(&dc->spin_mutex, flags); + printk("noz: shutdown %p\n", tport); +} - if (port->count == 0) { - DBG1("close: %d", nport->token_dl); - tty_port_tty_set(port, NULL); - spin_lock_irqsave(&dc->spin_mutex, flags); - dc->last_ier &= ~(nport->token_dl); - writew(dc->last_ier, dc->reg_ier); - spin_unlock_irqrestore(&dc->spin_mutex, flags); - } - mutex_unlock(&nport->tty_sem); +static void ntty_close(struct tty_struct *tty, struct file *filp) +{ + struct port *port = tty->driver_data; + if (port) + tty_port_close(&port->port, tty, filp); +} + +static void ntty_hangup(struct tty_struct *tty) +{ + struct port *port = tty->driver_data; + tty_port_hangup(&port->port); } /* @@ -1906,10 +1919,16 @@ exit_in_buffer: return rval; } +static const struct tty_port_operations noz_tty_port_ops = { + .activate = ntty_activate, + .shutdown = ntty_shutdown, +}; + static const struct tty_operations tty_ops = { .ioctl = ntty_ioctl, .open = ntty_open, .close = ntty_close, + .hangup = ntty_hangup, .write = ntty_write, .write_room = ntty_write_room, .unthrottle = ntty_unthrottle, @@ -1917,6 +1936,8 @@ static const struct tty_operations tty_ops = { .chars_in_buffer = ntty_chars_in_buffer, .tiocmget = ntty_tiocmget, .tiocmset = ntty_tiocmset, + .install = ntty_install, + .cleanup = ntty_cleanup, }; /* Module initialization */ -- cgit v1.2.3 From e8c65d143a9903cb553c0d80c819428c5b839a02 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 8 Feb 2010 10:07:04 +0000 Subject: nozomi: Fix mutex handling The original author didn't realise the kernel lock was a drop while sleep lock so did clever (and wrong) things to work around the non need to avoid deadlocks. Remove the cleverness and the comment (as we don't hold the BKL now anyway in those paths) Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/nozomi.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index cd405bcf53a6..fac9157a0ec0 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1693,15 +1693,7 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, if (!dc || !port) return -ENODEV; - if (unlikely(!mutex_trylock(&port->tty_sem))) { - /* - * must test lock as tty layer wraps calls - * to this function with BKL - */ - dev_err(&dc->pdev->dev, "Would have deadlocked - " - "return EAGAIN\n"); - return -EAGAIN; - } + mutex_lock(&port->tty_sem); if (unlikely(!port->port.count)) { DBG1(" "); @@ -1741,25 +1733,23 @@ exit: * This method is called by the upper tty layer. * #according to sources N_TTY.c it expects a value >= 0 and * does not check for negative values. + * + * If the port is unplugged report lots of room and let the bits + * dribble away so we don't block anything. */ static int ntty_write_room(struct tty_struct *tty) { struct port *port = tty->driver_data; - int room = 0; + int room = 4096; const struct nozomi *dc = get_dc_by_tty(tty); - if (!dc || !port) - return 0; - if (!mutex_trylock(&port->tty_sem)) - return 0; - - if (!port->port.count) - goto exit; - - room = port->fifo_ul.size - kfifo_len(&port->fifo_ul); - -exit: - mutex_unlock(&port->tty_sem); + if (dc) { + mutex_lock(&port->tty_sem); + if (port->port.count) + room = port->fifo_ul.size - + kfifo_len(&port->fifo_ul); + mutex_unlock(&port->tty_sem); + } return room; } -- cgit v1.2.3 From b2a3dbc3ed401828c4de0f08d08d96d0f5ea5b0b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 8 Feb 2010 10:07:15 +0000 Subject: nozomi: Tidy up the PCI table Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/nozomi.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index fac9157a0ec0..584910ff725e 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -136,10 +136,6 @@ static int debug; #define RECEIVE_BUF_MAX 4 -/* Define all types of vendors and devices to support */ -#define VENDOR1 0x1931 /* Vendor Option */ -#define DEVICE1 0x000c /* HSDPA card */ - #define R_IIR 0x0000 /* Interrupt Identity Register */ #define R_FCR 0x0000 /* Flow Control Register */ #define R_IER 0x0004 /* Interrupt Enable Register */ @@ -407,7 +403,7 @@ struct buffer { /* Global variables */ static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = { - {PCI_DEVICE(VENDOR1, DEVICE1)}, + {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ {}, }; -- cgit v1.2.3 From 638b9648ab51c9c549ff5735d3de519ef6199df3 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 8 Feb 2010 10:09:26 +0000 Subject: tty: Fix the ldisc hangup race This was noticed by Matthias Urlichs and he proposed a fix. This patch does the fixing a different way to avoid introducing several new race conditions into the code. The problem case is TTY_DRIVER_RESET_TERMIOS = 0. In that case while we abort the ldisc change, the hangup processing has not cleaned up and restarted the ldisc either. We can't restart the ldisc stuff in the set_ldisc as we don't know what the hangup did and may touch stuff we shouldn't as we are no longer supposed to influence the tty at that point in case it has been re-opened before we get rescheduled. Instead do it the simple way. Always re-init the ldisc on the hangup, but use TTY_DRIVER_RESET_TERMIOS to indicate that we should force N_TTY. Signed-off-by: Alan Cox Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_ldisc.c | 50 +++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 20 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index 3f653f7d849f..500e740ec5e4 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -706,12 +706,13 @@ static void tty_reset_termios(struct tty_struct *tty) /** * tty_ldisc_reinit - reinitialise the tty ldisc * @tty: tty to reinit + * @ldisc: line discipline to reinitialize * - * Switch the tty back to N_TTY line discipline and leave the - * ldisc state closed + * Switch the tty to a line discipline and leave the ldisc + * state closed */ -static void tty_ldisc_reinit(struct tty_struct *tty) +static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) { struct tty_ldisc *ld; @@ -721,10 +722,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty) /* * Switch the line discipline back */ - ld = tty_ldisc_get(N_TTY); + ld = tty_ldisc_get(ldisc); BUG_ON(IS_ERR(ld)); tty_ldisc_assign(tty, ld); - tty_set_termios_ldisc(tty, N_TTY); + tty_set_termios_ldisc(tty, ldisc); } /** @@ -745,6 +746,8 @@ static void tty_ldisc_reinit(struct tty_struct *tty) void tty_ldisc_hangup(struct tty_struct *tty) { struct tty_ldisc *ld; + int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; + int err = 0; /* * FIXME! What are the locking issues here? This may me overdoing @@ -772,25 +775,32 @@ void tty_ldisc_hangup(struct tty_struct *tty) wake_up_interruptible_poll(&tty->read_wait, POLLIN); /* * Shutdown the current line discipline, and reset it to - * N_TTY. + * N_TTY if need be. + * + * Avoid racing set_ldisc or tty_ldisc_release */ - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* Avoid racing set_ldisc or tty_ldisc_release */ - mutex_lock(&tty->ldisc_mutex); - tty_ldisc_halt(tty); - if (tty->ldisc) { /* Not yet closed */ - /* Switch back to N_TTY */ - tty_ldisc_reinit(tty); - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is - a FIXME */ + mutex_lock(&tty->ldisc_mutex); + tty_ldisc_halt(tty); + /* At this point we have a closed ldisc and we want to + reopen it. We could defer this to the next open but + it means auditing a lot of other paths so this is + a FIXME */ + if (tty->ldisc) { /* Not yet closed */ + if (reset == 0) { + tty_ldisc_reinit(tty, tty->termios->c_line); + err = tty_ldisc_open(tty, tty->ldisc); + } + /* If the re-open fails or we reset then go to N_TTY. The + N_TTY open cannot fail */ + if (reset || err) { + tty_ldisc_reinit(tty, N_TTY); WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - tty_ldisc_enable(tty); } - mutex_unlock(&tty->ldisc_mutex); - tty_reset_termios(tty); + tty_ldisc_enable(tty); } + mutex_unlock(&tty->ldisc_mutex); + if (reset) + tty_reset_termios(tty); } /** -- cgit v1.2.3 From 4165fe4ef7305609a96c7f248cefb9c414d0ede5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 17 Feb 2010 13:07:13 +0000 Subject: tty: Fix up char drivers request_room usage We can't change them all but quite a few misuse it. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/nozomi.c | 2 -- drivers/char/serial167.c | 3 +-- drivers/char/specialix.c | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 584910ff725e..a3f32a15fde4 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -853,8 +853,6 @@ static int receive_data(enum port_type index, struct nozomi *dc) goto put; } - tty_buffer_request_room(tty, size); - while (size > 0) { read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX); diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 452370af95de..986aa606a6b6 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -658,8 +658,7 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) info->mon.char_max = char_count; info->mon.char_last = char_count; #endif - len = tty_buffer_request_room(tty, char_count); - while (len--) { + while (char_count--) { data = base_addr[CyRDR]; tty_insert_flip_char(tty, data, TTY_NORMAL); #ifdef CYCLOM_16Y_HACK diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 268e17f9ec3f..07ac14d949ce 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -646,8 +646,6 @@ static void sx_receive(struct specialix_board *bp) dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count); port->hits[count > 8 ? 9 : count]++; - tty_buffer_request_room(tty, count); - while (count--) tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL); tty_flip_buffer_push(tty); -- cgit v1.2.3 From d9661adfb8e53a7647360140af3b92284cbe52d4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 18 Feb 2010 16:43:47 +0000 Subject: tty: Keep the default buffering to sub-page units We allocate during interrupts so while our buffering is normally diced up small anyway on some hardware at speed we can pressure the VM excessively for page pairs. We don't really need big buffers to be linear so don't try so hard. In order to make this work well we will tidy up excess callers to request_room, which cannot itself enforce this break up. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_buffer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 66fa4e10d76b..f27c4d6d956e 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -247,7 +247,8 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, { int copied = 0; do { - int space = tty_buffer_request_room(tty, size - copied); + int goal = min(size - copied, TTY_BUFFER_PAGE); + int space = tty_buffer_request_room(tty, goal); struct tty_buffer *tb = tty->buf.tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) @@ -283,7 +284,8 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, { int copied = 0; do { - int space = tty_buffer_request_room(tty, size - copied); + int goal = min(size - copied, TTY_BUFFER_PAGE); + int space = tty_buffer_request_room(tty, goal); struct tty_buffer *tb = tty->buf.tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) -- cgit v1.2.3 From eec9fe7d1ab4a0dfac4cb43047a7657fffd0002f Mon Sep 17 00:00:00 2001 From: Ari Entlich Date: Fri, 19 Feb 2010 09:37:55 -0500 Subject: tty: Add a new VT mode which is like VT_PROCESS but doesn't require a VT_RELDISP ioctl call This new VT mode (VT_PROCESS_AUTO) does everything that VT_PROCESS does except that it doesn't wait for a VT_RELDISP ioctl before switching away from a VT with that mode. If the X server eventually uses this new mode, debugging and crash recovery should become easier. This is because even when currently in the VT of a frozen X server it would still be possible to switch out by doing SysRq-r and then CTRL-, sshing in and doing chvt , or any other method of VT switching. The general concensus on #xorg-devel seems to be that it should be safe to use this with X now that we have KMS. This also moves the VT_ACKACQ define to a more appropriate place, for clarity's sake. Signed-off-by: Ari Entlich Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt_ioctl.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 6aa10284104a..87778dcf8727 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -888,7 +888,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ret = -EFAULT; goto out; } - if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) { + if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS && tmp.mode != VT_PROCESS_AUTO) { ret = -EINVAL; goto out; } @@ -1622,7 +1622,7 @@ static void complete_change_console(struct vc_data *vc) * telling it that it has acquired. Also check if it has died and * clean up (similar to logic employed in change_console()) */ - if (vc->vt_mode.mode == VT_PROCESS) { + if (vc->vt_mode.mode == VT_PROCESS || vc->vt_mode.mode == VT_PROCESS_AUTO) { /* * Send the signal as privileged - kill_pid() will * tell us if the process has gone or something else @@ -1682,7 +1682,7 @@ void change_console(struct vc_data *new_vc) * vt to auto control. */ vc = vc_cons[fg_console].d; - if (vc->vt_mode.mode == VT_PROCESS) { + if (vc->vt_mode.mode == VT_PROCESS || vc->vt_mode.mode == VT_PROCESS_AUTO) { /* * Send the signal as privileged - kill_pid() will * tell us if the process has gone or something else @@ -1693,27 +1693,28 @@ void change_console(struct vc_data *new_vc) */ vc->vt_newvt = new_vc->vc_num; if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) { + if(vc->vt_mode.mode == VT_PROCESS) + /* + * It worked. Mark the vt to switch to and + * return. The process needs to send us a + * VT_RELDISP ioctl to complete the switch. + */ + return; + } else { /* - * It worked. Mark the vt to switch to and - * return. The process needs to send us a - * VT_RELDISP ioctl to complete the switch. + * The controlling process has died, so we revert back to + * normal operation. In this case, we'll also change back + * to KD_TEXT mode. I'm not sure if this is strictly correct + * but it saves the agony when the X server dies and the screen + * remains blanked due to KD_GRAPHICS! It would be nice to do + * this outside of VT_PROCESS but there is no single process + * to account for and tracking tty count may be undesirable. */ - return; + reset_vc(vc); } /* - * The controlling process has died, so we revert back to - * normal operation. In this case, we'll also change back - * to KD_TEXT mode. I'm not sure if this is strictly correct - * but it saves the agony when the X server dies and the screen - * remains blanked due to KD_GRAPHICS! It would be nice to do - * this outside of VT_PROCESS but there is no single process - * to account for and tracking tty count may be undesirable. - */ - reset_vc(vc); - - /* - * Fall through to normal (VT_AUTO) handling of the switch... + * Fall through to normal (VT_AUTO and VT_PROCESS_AUTO) handling of the switch... */ } -- cgit v1.2.3 From 2832fc11f1360668482beec06dbcd631ae5f0cf1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 18 Feb 2010 16:43:54 +0000 Subject: USB: tty: Add a function to insert a string of characters with the same flag The USB drivers often want to insert a series of bytes all with the same flag set - provide a helper for this case. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_buffer.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 66fa4e10d76b..4c133459ab7e 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -231,9 +231,10 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) EXPORT_SYMBOL_GPL(tty_buffer_request_room); /** - * tty_insert_flip_string - Add characters to the tty buffer + * tty_insert_flip_string_fixed_flag - Add characters to the tty buffer * @tty: tty structure * @chars: characters + * @flag: flag value for each character * @size: size * * Queue a series of bytes to the tty buffering. All the characters @@ -242,8 +243,8 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Locking: Called functions may take tty->buf.lock */ -int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, - size_t size) +int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, + const unsigned char *chars, char flag, size_t size) { int copied = 0; do { @@ -253,7 +254,7 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, if (unlikely(space == 0)) break; memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + memset(tb->flag_buf_ptr + tb->used, flag, space); tb->used += space; copied += space; chars += space; @@ -262,7 +263,7 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, } while (unlikely(size > copied)); return copied; } -EXPORT_SYMBOL(tty_insert_flip_string); +EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); /** * tty_insert_flip_string_flags - Add characters to the tty buffer -- cgit v1.2.3 From 604b2ad7ccb11569d3b843bb1ce0fbe034e70769 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 24 Feb 2010 10:36:51 +0530 Subject: virtio: console: Fix type of 'len' as unsigned int We declare 'len' as int type but it should be 'unsigned int', as get_buf() wants it to be. Signed-off-by: Amit Shah Reported-by: Juan Quintela --- drivers/char/virtio_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 213373b5f17f..2bd6a9c302c8 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -379,7 +379,7 @@ static ssize_t send_control_msg(struct port *port, unsigned int event, struct scatterlist sg[1]; struct virtio_console_control cpkt; struct virtqueue *vq; - int len; + unsigned int len; if (!use_multiport(port->portdev)) return 0; -- cgit v1.2.3 From 335a64a5c21ed58de21c0130c90c7e647cdcf572 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 24 Feb 2010 10:37:44 +0530 Subject: virtio: console: Use better variable names for fill_queue operation We want to keep track of the number of buffers added to a vq. Use nr_added_bufs instead of 'ret'. Also, the users of fill_queue() overloaded a local 'err' variable to check the numbers of buffers allocated. Use nr_added_bufs instead of err. Signed-off-by: Amit Shah Reported-by: Juan Quintela --- drivers/char/virtio_console.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2bd6a9c302c8..f404ccfc9c20 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1071,27 +1071,27 @@ static void config_intr(struct virtio_device *vdev) static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) { struct port_buffer *buf; - unsigned int ret; - int err; + unsigned int nr_added_bufs; + int ret; - ret = 0; + nr_added_bufs = 0; do { buf = alloc_buf(PAGE_SIZE); if (!buf) break; spin_lock_irq(lock); - err = add_inbuf(vq, buf); - if (err < 0) { + ret = add_inbuf(vq, buf); + if (ret < 0) { spin_unlock_irq(lock); free_buf(buf); break; } - ret++; + nr_added_bufs++; spin_unlock_irq(lock); - } while (err > 0); + } while (ret > 0); - return ret; + return nr_added_bufs; } static int add_port(struct ports_device *portdev, u32 id) @@ -1100,6 +1100,7 @@ static int add_port(struct ports_device *portdev, u32 id) struct port *port; struct port_buffer *buf; dev_t devt; + unsigned int nr_added_bufs; int err; port = kmalloc(sizeof(*port), GFP_KERNEL); @@ -1144,8 +1145,8 @@ static int add_port(struct ports_device *portdev, u32 id) init_waitqueue_head(&port->waitqueue); /* Fill the in_vq with buffers so the host can send us data. */ - err = fill_queue(port->in_vq, &port->inbuf_lock); - if (!err) { + nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); + if (!nr_added_bufs) { dev_err(port->dev, "Error allocating inbufs\n"); err = -ENOMEM; goto free_device; @@ -1442,12 +1443,14 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) INIT_LIST_HEAD(&portdev->ports); if (multiport) { + unsigned int nr_added_bufs; + spin_lock_init(&portdev->cvq_lock); INIT_WORK(&portdev->control_work, &control_work_handler); INIT_WORK(&portdev->config_work, &config_work_handler); - err = fill_queue(portdev->c_ivq, &portdev->cvq_lock); - if (!err) { + nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); + if (!nr_added_bufs) { dev_err(&vdev->dev, "Error allocating buffers for control queue\n"); err = -ENOMEM; -- cgit v1.2.3