llseek: automatically add .llseek fop
[linux-2.6.git] / drivers / block / aoe / aoechr.c
index 0c543d3..32b484b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006 Coraid, Inc.  See COPYING for GPL terms. */
+/* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
 /*
  * aoechr.c
  * AoE character device driver
@@ -6,6 +6,11 @@
 
 #include <linux/hdreg.h>
 #include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/skbuff.h>
 #include "aoe.h"
 
 enum {
@@ -14,6 +19,7 @@ enum {
        MINOR_DISCOVER,
        MINOR_INTERFACES,
        MINOR_REVALIDATE,
+       MINOR_FLUSH,
        MSGSZ = 2048,
        NMSG = 100,             /* message backlog to retain */
 };
@@ -33,7 +39,7 @@ struct ErrMsg {
 
 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;
@@ -42,6 +48,7 @@ static struct aoe_chardev chardevs[] = {
        { MINOR_DISCOVER, "discover" },
        { MINOR_INTERFACES, "interfaces" },
        { MINOR_REVALIDATE, "revalidate" },
+       { MINOR_FLUSH, "flush" },
 };
 
 static int
@@ -55,9 +62,8 @@ 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;
@@ -69,6 +75,7 @@ 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)
@@ -80,19 +87,31 @@ revalidate(const char __user *str, size_t size)
        /* should be e%d.%d format */
        n = sscanf(buf, "e%d.%d", &major, &minor);
        if (n != 2) {
-               printk(KERN_ERR "aoe: %s: invalid device specification\n",
-                       __FUNCTION__);
+               printk(KERN_ERR "aoe: invalid device specification\n");
                return -EINVAL;
        }
        d = aoedev_by_aoeaddr(major, minor);
        if (!d)
                return -EINVAL;
-
        spin_lock_irqsave(&d->lock, flags);
-       d->flags |= DEVFL_PAUSE;
+       aoecmd_cleanslate(d);
+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(200)) {
+               spin_lock_irqsave(&d->lock, flags);
+               goto loop;
+       }
+       if (skb) {
+               struct sk_buff_head queue;
+               __skb_queue_head_init(&queue);
+               __skb_queue_tail(&queue, skb);
+               aoenet_xmit(&queue);
+       }
        aoecmd_cfg(major, minor);
-
        return 0;
 }
 
@@ -115,7 +134,7 @@ bail:               spin_unlock_irqrestore(&emsgs_lock, flags);
 
        mp = kmalloc(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;
        }
 
@@ -130,7 +149,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
@@ -140,7 +159,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();
@@ -150,6 +169,9 @@ aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp
                break;
        case MINOR_REVALIDATE:
                ret = revalidate(buf, cnt);
+               break;
+       case MINOR_FLUSH:
+               ret = aoedev_flush(buf, cnt);
        }
        if (ret == 0)
                ret = cnt;
@@ -161,12 +183,16 @@ aoechr_open(struct inode *inode, struct file *filp)
 {
        int n, i;
 
+       lock_kernel();
        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) {
+                       unlock_kernel();
                        return 0;
+               }
+       unlock_kernel();
        return -EINVAL;
 }
 
@@ -186,62 +212,67 @@ 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, mode_t *mode)
+{
+       return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
+}
+
 int __init
 aoechr_init(void)
 {
@@ -249,20 +280,22 @@ aoechr_init(void)
 
        n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
        if (n < 0) { 
-               printk(KERN_ERR "aoe: aoechr_init: can't register char device\n");
+               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;
 }
@@ -273,7 +306,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");
 }