Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / block / aoe / aoedev.c
index e26f6f4..eeea477 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. */
 /*
  * aoedev.c
  * AoE device utility functions; maintains device list.
@@ -7,32 +7,16 @@
 #include <linux/hdreg.h>
 #include <linux/blkdev.h>
 #include <linux/netdevice.h>
+#include <linux/delay.h>
 #include "aoe.h"
 
 static void dummy_timer(ulong);
 static void aoedev_freedev(struct aoedev *);
-static void freetgt(struct aoetgt *t);
+static void freetgt(struct aoedev *d, struct aoetgt *t);
+static void skbpoolfree(struct aoedev *d);
 
 static struct aoedev *devlist;
-static spinlock_t devlist_lock;
-
-int
-aoedev_isbusy(struct aoedev *d)
-{
-       struct aoetgt **t, **te;
-       struct frame *f, *e;
-
-       t = d->targets;
-       te = t + NTARGETS;
-       for (; t < te && *t; t++) {
-               f = (*t)->frames;
-               e = f + (*t)->nframes;
-               for (; f < e; f++)
-                       if (f->tag != FREETAG)
-                               return 1;
-       }
-       return 0;
-}
+static DEFINE_SPINLOCK(devlist_lock);
 
 struct aoedev *
 aoedev_by_aoeaddr(int maj, int min)
@@ -107,7 +91,7 @@ aoedev_downdev(struct aoedev *d)
        }
 
        if (d->gd)
-               d->gd->capacity = 0;
+               set_capacity(d->gd, 0);
 
        d->flags &= ~DEVFL_UP;
 }
@@ -125,9 +109,10 @@ aoedev_freedev(struct aoedev *d)
        t = d->targets;
        e = t + NTARGETS;
        for (; t < e && *t; t++)
-               freetgt(*t);
+               freetgt(d, *t);
        if (d->bufpool)
                mempool_destroy(d->bufpool);
+       skbpoolfree(d);
        kfree(d);
 }
 
@@ -176,6 +161,41 @@ aoedev_flush(const char __user *str, size_t cnt)
        return 0;
 }
 
+/* I'm not really sure that this is a realistic problem, but if the
+network driver goes gonzo let's just leak memory after complaining. */
+static void
+skbfree(struct sk_buff *skb)
+{
+       enum { Sms = 100, Tms = 3*1000};
+       int i = Tms / Sms;
+
+       if (skb == NULL)
+               return;
+       while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0)
+               msleep(Sms);
+       if (i < 0) {
+               printk(KERN_ERR
+                       "aoe: %s holds ref: %s\n",
+                       skb->dev ? skb->dev->name : "netif",
+                       "cannot free skb -- memory leaked.");
+               return;
+       }
+       skb_shinfo(skb)->nr_frags = skb->data_len = 0;
+       skb_trim(skb, 0);
+       dev_kfree_skb(skb);
+}
+
+static void
+skbpoolfree(struct aoedev *d)
+{
+       struct sk_buff *skb, *tmp;
+
+       skb_queue_walk_safe(&d->skbpool, skb, tmp)
+               skbfree(skb);
+
+       __skb_queue_head_init(&d->skbpool);
+}
+
 /* find it or malloc it */
 struct aoedev *
 aoedev_by_sysminor_m(ulong sysminor)
@@ -195,6 +215,8 @@ aoedev_by_sysminor_m(ulong sysminor)
                goto out;
        INIT_WORK(&d->work, aoecmd_sleepwork);
        spin_lock_init(&d->lock);
+       skb_queue_head_init(&d->sendq);
+       skb_queue_head_init(&d->skbpool);
        init_timer(&d->timer);
        d->timer.data = (ulong) d;
        d->timer.function = dummy_timer;
@@ -215,16 +237,14 @@ aoedev_by_sysminor_m(ulong sysminor)
 }
 
 static void
-freetgt(struct aoetgt *t)
+freetgt(struct aoedev *d, struct aoetgt *t)
 {
        struct frame *f, *e;
 
        f = t->frames;
        e = f + t->nframes;
-       for (; f < e; f++) {
-               skb_shinfo(f->skb)->nr_frags = 0;
-               dev_kfree_skb(f->skb);
-       }
+       for (; f < e; f++)
+               skbfree(f->skb);
        kfree(t->frames);
        kfree(t);
 }
@@ -253,7 +273,5 @@ aoedev_exit(void)
 int __init
 aoedev_init(void)
 {
-       spin_lock_init(&devlist_lock);
        return 0;
 }
-