diff options
Diffstat (limited to 'drivers/block/aoe/aoechr.c')
| -rw-r--r-- | drivers/block/aoe/aoechr.c | 188 |
1 files changed, 132 insertions, 56 deletions
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c index 41ae0ede619..ab41be625a5 100644 --- a/drivers/block/aoe/aoechr.c +++ b/drivers/block/aoe/aoechr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */ /* * aoechr.c * AoE character device driver @@ -6,6 +6,12 @@ #include <linux/hdreg.h> #include <linux/blkdev.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/skbuff.h> +#include <linux/export.h> #include "aoe.h" enum { @@ -13,8 +19,9 @@ enum { MINOR_ERR = 2, MINOR_DISCOVER, MINOR_INTERFACES, + MINOR_REVALIDATE, + MINOR_FLUSH, MSGSZ = 2048, - NARGS = 10, NMSG = 100, /* message backlog to retain */ }; @@ -31,9 +38,15 @@ struct ErrMsg { char *msg; }; +static DEFINE_MUTEX(aoechr_mutex); + +/* A ring buffer of error messages, to be read through + * "/dev/etherd/err". When no messages are present, + * readers will block waiting for messages to appear. + */ static struct ErrMsg emsgs[NMSG]; static int emsgs_head_idx, emsgs_tail_idx; -static struct semaphore emsgs_sema; +static struct completion emsgs_comp; static spinlock_t emsgs_lock; static int nblocked_emsgs_readers; static struct class *aoe_class; @@ -41,6 +54,8 @@ static struct aoe_chardev chardevs[] = { { MINOR_ERR, "err" }, { MINOR_DISCOVER, "discover" }, { MINOR_INTERFACES, "interfaces" }, + { MINOR_REVALIDATE, "revalidate" }, + { MINOR_FLUSH, "flush" }, }; static int @@ -54,10 +69,55 @@ static int interfaces(const char __user *str, size_t size) { if (set_aoe_iflist(str, size)) { - printk(KERN_CRIT - "%s: could not set interface list: %s\n", - __FUNCTION__, "too many interfaces"); + printk(KERN_ERR + "aoe: could not set interface list: too many interfaces\n"); + return -EINVAL; + } + return 0; +} + +static int +revalidate(const char __user *str, size_t size) +{ + int major, minor, n; + ulong flags; + struct aoedev *d; + struct sk_buff *skb; + char buf[16]; + + if (size >= sizeof buf) + return -EINVAL; + buf[sizeof buf - 1] = '\0'; + if (copy_from_user(buf, str, size)) + return -EFAULT; + + n = sscanf(buf, "e%d.%d", &major, &minor); + if (n != 2) { + pr_err("aoe: invalid device specification %s\n", buf); + return -EINVAL; + } + d = aoedev_by_aoeaddr(major, minor, 0); + if (!d) return -EINVAL; + spin_lock_irqsave(&d->lock, flags); + aoecmd_cleanslate(d); + aoecmd_cfg(major, minor); +loop: + skb = aoecmd_ata_id(d); + spin_unlock_irqrestore(&d->lock, flags); + /* try again if we are able to sleep a bit, + * otherwise give up this revalidation + */ + if (!skb && !msleep_interruptible(250)) { + spin_lock_irqsave(&d->lock, flags); + goto loop; + } + aoedev_put(d); + if (skb) { + struct sk_buff_head queue; + __skb_queue_head_init(&queue); + __skb_queue_tail(&queue, skb); + aoenet_xmit(&queue); } return 0; } @@ -79,13 +139,12 @@ bail: spin_unlock_irqrestore(&emsgs_lock, flags); return; } - mp = kmalloc(n, GFP_ATOMIC); + mp = kmemdup(msg, n, GFP_ATOMIC); if (mp == NULL) { - printk(KERN_CRIT "aoe: aoechr_error: allocation failure, len=%ld\n", n); + printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n); goto bail; } - memcpy(mp, msg, n); em->msg = mp; em->flags |= EMFL_VALID; em->len = n; @@ -96,7 +155,7 @@ bail: spin_unlock_irqrestore(&emsgs_lock, flags); spin_unlock_irqrestore(&emsgs_lock, flags); if (nblocked_emsgs_readers) - up(&emsgs_sema); + complete(&emsgs_comp); } static ssize_t @@ -106,7 +165,7 @@ aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp switch ((unsigned long) filp->private_data) { default: - printk(KERN_INFO "aoe: aoechr_write: can't write to that file.\n"); + printk(KERN_INFO "aoe: can't write to that file.\n"); break; case MINOR_DISCOVER: ret = discover(); @@ -114,6 +173,12 @@ aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp case MINOR_INTERFACES: ret = interfaces(buf, cnt); break; + case MINOR_REVALIDATE: + ret = revalidate(buf, cnt); + break; + case MINOR_FLUSH: + ret = aoedev_flush(buf, cnt); + break; } if (ret == 0) ret = cnt; @@ -125,12 +190,16 @@ aoechr_open(struct inode *inode, struct file *filp) { int n, i; - n = MINOR(inode->i_rdev); + mutex_lock(&aoechr_mutex); + n = iminor(inode); filp->private_data = (void *) (unsigned long) n; for (i = 0; i < ARRAY_SIZE(chardevs); ++i) - if (chardevs[i].minor == n) + if (chardevs[i].minor == n) { + mutex_unlock(&aoechr_mutex); return 0; + } + mutex_unlock(&aoechr_mutex); return -EINVAL; } @@ -150,83 +219,90 @@ aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) ulong flags; n = (unsigned long) filp->private_data; - switch (n) { - case MINOR_ERR: - spin_lock_irqsave(&emsgs_lock, flags); -loop: - em = emsgs + emsgs_head_idx; - if ((em->flags & EMFL_VALID) == 0) { - if (filp->f_flags & O_NDELAY) { - spin_unlock_irqrestore(&emsgs_lock, flags); - return -EAGAIN; - } - nblocked_emsgs_readers++; + if (n != MINOR_ERR) + return -EFAULT; + + spin_lock_irqsave(&emsgs_lock, flags); + for (;;) { + em = emsgs + emsgs_head_idx; + if ((em->flags & EMFL_VALID) != 0) + break; + if (filp->f_flags & O_NDELAY) { spin_unlock_irqrestore(&emsgs_lock, flags); + return -EAGAIN; + } + nblocked_emsgs_readers++; - n = down_interruptible(&emsgs_sema); + spin_unlock_irqrestore(&emsgs_lock, flags); - spin_lock_irqsave(&emsgs_lock, flags); + n = wait_for_completion_interruptible(&emsgs_comp); - nblocked_emsgs_readers--; + spin_lock_irqsave(&emsgs_lock, flags); - if (n) { - spin_unlock_irqrestore(&emsgs_lock, flags); - return -ERESTARTSYS; - } - goto loop; - } - if (em->len > cnt) { + nblocked_emsgs_readers--; + + if (n) { spin_unlock_irqrestore(&emsgs_lock, flags); - return -EAGAIN; + return -ERESTARTSYS; } - mp = em->msg; - len = em->len; - em->msg = NULL; - em->flags &= ~EMFL_VALID; + } + if (em->len > cnt) { + spin_unlock_irqrestore(&emsgs_lock, flags); + return -EAGAIN; + } + mp = em->msg; + len = em->len; + em->msg = NULL; + em->flags &= ~EMFL_VALID; - emsgs_head_idx++; - emsgs_head_idx %= ARRAY_SIZE(emsgs); + emsgs_head_idx++; + emsgs_head_idx %= ARRAY_SIZE(emsgs); - spin_unlock_irqrestore(&emsgs_lock, flags); + spin_unlock_irqrestore(&emsgs_lock, flags); - n = copy_to_user(buf, mp, len); - kfree(mp); - return n == 0 ? len : -EFAULT; - default: - return -EFAULT; - } + n = copy_to_user(buf, mp, len); + kfree(mp); + return n == 0 ? len : -EFAULT; } -static struct file_operations aoe_fops = { +static const struct file_operations aoe_fops = { .write = aoechr_write, .read = aoechr_read, .open = aoechr_open, .release = aoechr_rel, .owner = THIS_MODULE, + .llseek = noop_llseek, }; +static char *aoe_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev)); +} + int __init aoechr_init(void) { int n, i; n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops); - if (n < 0) { - printk(KERN_ERR "aoe: aoechr_init: can't register char device\n"); + if (n < 0) { + printk(KERN_ERR "aoe: can't register char device\n"); return n; } - sema_init(&emsgs_sema, 0); + init_completion(&emsgs_comp); spin_lock_init(&emsgs_lock); aoe_class = class_create(THIS_MODULE, "aoe"); if (IS_ERR(aoe_class)) { unregister_chrdev(AOE_MAJOR, "aoechr"); return PTR_ERR(aoe_class); } + aoe_class->devnode = aoe_devnode; + for (i = 0; i < ARRAY_SIZE(chardevs); ++i) - class_device_create(aoe_class, NULL, - MKDEV(AOE_MAJOR, chardevs[i].minor), - NULL, chardevs[i].name); + device_create(aoe_class, NULL, + MKDEV(AOE_MAJOR, chardevs[i].minor), NULL, + chardevs[i].name); return 0; } @@ -237,7 +313,7 @@ aoechr_exit(void) int i; for (i = 0; i < ARRAY_SIZE(chardevs); ++i) - class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor)); + device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor)); class_destroy(aoe_class); unregister_chrdev(AOE_MAJOR, "aoechr"); } |
