aoe: support more AoE addresses with dynamic block device minor numbers
[deliverable/linux.git] / drivers / block / aoe / aoedev.c
index ccaecff4c69b1514dce4405d9e293217f23e8c7c..68a7a5a9ced0c6763ae7b97042b89559d2d76583 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/netdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/bitmap.h>
+#include <linux/kdev_t.h>
 #include "aoe.h"
 
 static void dummy_timer(ulong);
@@ -19,35 +21,63 @@ static void skbpoolfree(struct aoedev *d);
 static struct aoedev *devlist;
 static DEFINE_SPINLOCK(devlist_lock);
 
-/*
- * Users who grab a pointer to the device with aoedev_by_aoeaddr or
- * aoedev_by_sysminor_m automatically get a reference count and must
- * be responsible for performing a aoedev_put.  With the addition of
- * async kthread processing I'm no longer confident that we can
- * guarantee consistency in the face of device flushes.
- *
- * For the time being, we only bother to add extra references for
- * frames sitting on the iocq.  When the kthreads finish processing
- * these frames, they will aoedev_put the device.
+/* Because some systems will have one, many, or no
+ *   - partitions,
+ *   - slots per shelf,
+ *   - or shelves,
+ * we need some flexibility in the way the minor numbers
+ * are allocated.  So they are dynamic.
  */
-struct aoedev *
-aoedev_by_aoeaddr(int maj, int min)
+#define N_DEVS ((1U<<MINORBITS)/AOE_PARTITIONS)
+
+static DEFINE_SPINLOCK(used_minors_lock);
+static DECLARE_BITMAP(used_minors, N_DEVS);
+
+static int
+minor_get(ulong *minor)
 {
-       struct aoedev *d;
        ulong flags;
+       ulong n;
+       int error = 0;
+
+       spin_lock_irqsave(&used_minors_lock, flags);
+       n = find_first_zero_bit(used_minors, N_DEVS);
+       if (n < N_DEVS)
+               set_bit(n, used_minors);
+       else
+               error = -1;
+       spin_unlock_irqrestore(&used_minors_lock, flags);
+
+       *minor = n * AOE_PARTITIONS;
+       return error;
+}
 
-       spin_lock_irqsave(&devlist_lock, flags);
+static void
+minor_free(ulong minor)
+{
+       ulong flags;
 
-       for (d=devlist; d; d=d->next)
-               if (d->aoemajor == maj && d->aoeminor == min) {
-                       d->ref++;
-                       break;
-               }
+       minor /= AOE_PARTITIONS;
+       BUG_ON(minor >= N_DEVS);
 
-       spin_unlock_irqrestore(&devlist_lock, flags);
-       return d;
+       spin_lock_irqsave(&used_minors_lock, flags);
+       BUG_ON(!test_bit(minor, used_minors));
+       clear_bit(minor, used_minors);
+       spin_unlock_irqrestore(&used_minors_lock, flags);
 }
 
+/*
+ * Users who grab a pointer to the device with aoedev_by_aoeaddr
+ * automatically get a reference count and must be responsible
+ * for performing a aoedev_put.  With the addition of async
+ * kthread processing I'm no longer confident that we can
+ * guarantee consistency in the face of device flushes.
+ *
+ * For the time being, we only bother to add extra references for
+ * frames sitting on the iocq.  When the kthreads finish processing
+ * these frames, they will aoedev_put the device.
+ */
+
 void
 aoedev_put(struct aoedev *d)
 {
@@ -159,6 +189,7 @@ aoedev_freedev(struct aoedev *d)
        if (d->bufpool)
                mempool_destroy(d->bufpool);
        skbpoolfree(d);
+       minor_free(d->sysminor);
        kfree(d);
 }
 
@@ -246,22 +277,23 @@ skbpoolfree(struct aoedev *d)
        __skb_queue_head_init(&d->skbpool);
 }
 
-/* find it or malloc it */
+/* find it or allocate it */
 struct aoedev *
-aoedev_by_sysminor_m(ulong sysminor)
+aoedev_by_aoeaddr(ulong maj, int min, int do_alloc)
 {
        struct aoedev *d;
        int i;
        ulong flags;
+       ulong sysminor;
 
        spin_lock_irqsave(&devlist_lock, flags);
 
        for (d=devlist; d; d=d->next)
-               if (d->sysminor == sysminor) {
+               if (d->aoemajor == maj && d->aoeminor == min) {
                        d->ref++;
                        break;
                }
-       if (d)
+       if (d || !do_alloc || minor_get(&sysminor) < 0)
                goto out;
        d = kcalloc(1, sizeof *d, GFP_ATOMIC);
        if (!d)
@@ -280,8 +312,8 @@ aoedev_by_sysminor_m(ulong sysminor)
        for (i = 0; i < NFACTIVE; i++)
                INIT_LIST_HEAD(&d->factive[i]);
        d->sysminor = sysminor;
-       d->aoemajor = AOEMAJOR(sysminor);
-       d->aoeminor = AOEMINOR(sysminor);
+       d->aoemajor = maj;
+       d->aoeminor = min;
        d->mintimer = MINTIMER;
        d->next = devlist;
        devlist = d;
This page took 0.028106 seconds and 5 git commands to generate.