From e3cc067b0a79d3a3672bfe7cfba12f2e8ae56039 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 18 Sep 2009 16:31:22 -0700 Subject: xen/evtchn: track enabled state for each port enable/disable_irq() complain if the enables/disables are unbalanced, so keep track of the state and avoid redundant enables. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 71 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 15 deletions(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index af03195..4356a9a 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -69,10 +69,36 @@ struct per_user_data { const char *name; }; -/* Who's bound to each port? */ -static struct per_user_data *port_user[NR_EVENT_CHANNELS]; +/* + * Who's bound to each port? This is logically an array of struct + * per_user_data *, but we encode the current enabled-state in bit 0. + */ +static unsigned long port_user[NR_EVENT_CHANNELS]; static DEFINE_SPINLOCK(port_user_lock); /* protects port_user[] and ring_prod */ +static inline struct per_user_data *get_port_user(unsigned port) +{ + return (struct per_user_data *)(port_user[port] & ~1); +} + +static inline void set_port_user(unsigned port, struct per_user_data *u) +{ + port_user[port] = (unsigned long)u; +} + +static inline bool get_port_enabled(unsigned port) +{ + return port_user[port] & 1; +} + +static inline void set_port_enabled(unsigned port, bool enabled) +{ + if (enabled) + port_user[port] |= 1; + else + port_user[port] &= ~1; +} + irqreturn_t evtchn_interrupt(int irq, void *data) { unsigned int port = (unsigned long)data; @@ -80,9 +106,15 @@ irqreturn_t evtchn_interrupt(int irq, void *data) spin_lock(&port_user_lock); - u = port_user[port]; + u = get_port_user(port); + + if (WARN(!get_port_enabled(port), + "Interrupt for port %d, but apparently not enabled; per-user %p\n", + port, u)) + goto out; disable_irq_nosync(irq); + set_port_enabled(port, false); if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) { u->ring[EVTCHN_RING_MASK(u->ring_prod)] = port; @@ -92,10 +124,10 @@ irqreturn_t evtchn_interrupt(int irq, void *data) kill_fasync(&u->evtchn_async_queue, SIGIO, POLL_IN); } - } else { + } else u->ring_overflow = 1; - } +out: spin_unlock(&port_user_lock); return IRQ_HANDLED; @@ -198,9 +230,18 @@ static ssize_t evtchn_write(struct file *file, const char __user *buf, goto out; spin_lock_irq(&port_user_lock); - for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) - if ((kbuf[i] < NR_EVENT_CHANNELS) && (port_user[kbuf[i]] == u)) - enable_irq(irq_from_evtchn(kbuf[i])); + + for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) { + unsigned port = kbuf[i]; + + if (port < NR_EVENT_CHANNELS && + get_port_user(port) == u && + !get_port_enabled(port)) { + set_port_enabled(port, true); + enable_irq(irq_from_evtchn(port)); + } + } + spin_unlock_irq(&port_user_lock); rc = count; @@ -222,8 +263,8 @@ static int evtchn_bind_to_user(struct per_user_data *u, int port) * interrupt handler yet, and our caller has already * serialized bind operations.) */ - BUG_ON(port_user[port] != NULL); - port_user[port] = u; + BUG_ON(get_port_user(port) != NULL); + set_port_user(port, u); rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED, u->name, (void *)(unsigned long)port); @@ -242,7 +283,7 @@ static void evtchn_unbind_from_user(struct per_user_data *u, int port) /* make sure we unbind the irq handler before clearing the port */ barrier(); - port_user[port] = NULL; + set_port_user(port, NULL); } static long evtchn_ioctl(struct file *file, @@ -333,7 +374,7 @@ static long evtchn_ioctl(struct file *file, spin_lock_irq(&port_user_lock); rc = -ENOTCONN; - if (port_user[unbind.port] != u) { + if (get_port_user(unbind.port) != u) { spin_unlock_irq(&port_user_lock); break; } @@ -355,7 +396,7 @@ static long evtchn_ioctl(struct file *file, if (notify.port >= NR_EVENT_CHANNELS) { rc = -EINVAL; - } else if (port_user[notify.port] != u) { + } else if (get_port_user(notify.port) != u) { rc = -ENOTCONN; } else { notify_remote_via_evtchn(notify.port); @@ -444,10 +485,10 @@ static int evtchn_release(struct inode *inode, struct file *filp) free_page((unsigned long)u->ring); for (i = 0; i < NR_EVENT_CHANNELS; i++) { - if (port_user[i] != u) + if (get_port_user(i) != u) continue; - evtchn_unbind_from_user(port_user[i], i); + evtchn_unbind_from_user(get_port_user(i), i); } spin_unlock_irq(&port_user_lock); -- cgit v1.1 From 93afe0b75ef3675ca05320919a57de8b9bbb159c Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 18 Sep 2009 16:36:58 -0700 Subject: xen/evtchn: dynamically allocate port_user array We only need the array when running as a Xen domain, so dynamically allocate it as needed to save on bss space. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 4356a9a..709c32d 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -73,7 +73,7 @@ struct per_user_data { * Who's bound to each port? This is logically an array of struct * per_user_data *, but we encode the current enabled-state in bit 0. */ -static unsigned long port_user[NR_EVENT_CHANNELS]; +static unsigned long *port_user; static DEFINE_SPINLOCK(port_user_lock); /* protects port_user[] and ring_prod */ static inline struct per_user_data *get_port_user(unsigned port) @@ -522,8 +522,11 @@ static int __init evtchn_init(void) if (!xen_domain()) return -ENODEV; + port_user = kcalloc(NR_EVENT_CHANNELS, sizeof(*port_user), GFP_KERNEL); + if (port_user == NULL) + return -ENOMEM; + spin_lock_init(&port_user_lock); - memset(port_user, 0, sizeof(port_user)); /* Create '/dev/misc/evtchn'. */ err = misc_register(&evtchn_miscdev); @@ -539,6 +542,9 @@ static int __init evtchn_init(void) static void __exit evtchn_cleanup(void) { + kfree(port_user); + port_user = NULL; + misc_deregister(&evtchn_miscdev); } -- cgit v1.1 From 0edce91dcd83160019867a00746c679344dc0bbd Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 18 Sep 2009 16:55:29 -0700 Subject: xen/evtchn: ports start enabled Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 709c32d..72dc7f2 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -108,10 +108,9 @@ irqreturn_t evtchn_interrupt(int irq, void *data) u = get_port_user(port); - if (WARN(!get_port_enabled(port), - "Interrupt for port %d, but apparently not enabled; per-user %p\n", - port, u)) - goto out; + WARN(!get_port_enabled(port), + "Interrupt for port %d, but apparently not enabled; per-user %p\n", + port, u); disable_irq_nosync(irq); set_port_enabled(port, false); @@ -127,7 +126,6 @@ irqreturn_t evtchn_interrupt(int irq, void *data) } else u->ring_overflow = 1; -out: spin_unlock(&port_user_lock); return IRQ_HANDLED; @@ -265,6 +263,7 @@ static int evtchn_bind_to_user(struct per_user_data *u, int port) */ BUG_ON(get_port_user(port) != NULL); set_port_user(port, u); + set_port_enabled(port, true); /* start enabled */ rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, IRQF_DISABLED, u->name, (void *)(unsigned long)port); -- cgit v1.1 From 1a1a17cddbfb1f81222b3f18ee8530c41fbc3b82 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 18 Sep 2009 17:13:41 -0700 Subject: xen/evtchn: remove spurious barrier evtchn_unbind_from_user() is called under a lock, so there's no need to worry about the ordering of unbind_from_irqhandler vs clearing the port per-user data. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 72dc7f2..f79ac5c 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -279,9 +279,6 @@ static void evtchn_unbind_from_user(struct per_user_data *u, int port) unbind_from_irqhandler(irq, (void *)(unsigned long)port); - /* make sure we unbind the irq handler before clearing the port */ - barrier(); - set_port_user(port, NULL); } -- cgit v1.1 From 3f5e554f669098c84c82ce75e7577f7e0f3fccde Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 28 May 2010 15:28:27 -0700 Subject: xen/evtchn: don't do unbind_from_irqhandler under spinlock unbind_from_irqhandler can end up doing /proc operations, which can't happen under a spinlock. So before removing the IRQ handler, disable the irq under the port_user lock (masking the underlying event channel and making sure the irq handler isn't running concurrently and won't start running), then remove the handler without the lock. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index f79ac5c..6a3a129 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -375,10 +375,12 @@ static long evtchn_ioctl(struct file *file, break; } - evtchn_unbind_from_user(u, unbind.port); + disable_irq(irq_from_evtchn(unbind.port)); spin_unlock_irq(&port_user_lock); + evtchn_unbind_from_user(u, unbind.port); + rc = 0; break; } @@ -484,11 +486,18 @@ static int evtchn_release(struct inode *inode, struct file *filp) if (get_port_user(i) != u) continue; - evtchn_unbind_from_user(get_port_user(i), i); + disable_irq(irq_from_evtchn(i)); } spin_unlock_irq(&port_user_lock); + for (i = 0; i < NR_EVENT_CHANNELS; i++) { + if (get_port_user(i) != u) + continue; + + evtchn_unbind_from_user(get_port_user(i), i); + } + kfree(u->name); kfree(u); -- cgit v1.1 From 376d908f52427591cef4acd172db9c3ef28676ec Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Fri, 28 May 2010 15:43:49 -0700 Subject: xen/evtchn: Fix name of Xen event-channel device The Xen event-channel device is named evtchn in the kernel but always used as /dev/xen/evtchn in userspace. This patch fixes the name. Signed-off-by: Bastian Blank Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 6a3a129..68119f6 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -517,7 +517,7 @@ static const struct file_operations evtchn_fops = { static struct miscdevice evtchn_miscdev = { .minor = MISC_DYNAMIC_MINOR, - .name = "evtchn", + .name = "xen/evtchn", .fops = &evtchn_fops, }; static int __init evtchn_init(void) -- cgit v1.1 From 70697d540c0598ad023a391d4c895044db9a6624 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 5 Oct 2010 11:13:44 -0700 Subject: xen/evtchn: add missing static Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/evtchn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/xen') diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 68119f6..f3594ec 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -99,7 +99,7 @@ static inline void set_port_enabled(unsigned port, bool enabled) port_user[port] &= ~1; } -irqreturn_t evtchn_interrupt(int irq, void *data) +static irqreturn_t evtchn_interrupt(int irq, void *data) { unsigned int port = (unsigned long)data; struct per_user_data *u; -- cgit v1.1