Commit 0c966214 authored by Ed Cashin's avatar Ed Cashin Committed by Linus Torvalds

aoe: support more AoE addresses with dynamic block device minor numbers

The ATA over Ethernet protocol uses a major (shelf) and minor (slot)
address to identify a particular storage target.  These changes remove an
artificial limitation the aoe driver imposes on the use of AoE addresses.
For example, without these changes, the slot address has a maximum of 15,
but users commonly use slot numbers much greater than that.

The AoE shelf and slot address space is often used sparsely.  Instead of
using a static mapping between AoE addresses and the block device minor
number, the block device minor numbers are now allocated on demand.
Signed-off-by: default avatarEd Cashin <ecashin@coraid.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent eecdf226
...@@ -49,6 +49,8 @@ struct aoe_hdr { ...@@ -49,6 +49,8 @@ struct aoe_hdr {
__be32 tag; __be32 tag;
}; };
#define AOE_MAXSHELF (0xffff-1) /* one less than the broadcast shelf address */
struct aoe_atahdr { struct aoe_atahdr {
unsigned char aflags; unsigned char aflags;
unsigned char errfeat; unsigned char errfeat;
...@@ -211,8 +213,7 @@ void aoe_ktstop(struct ktstate *k); ...@@ -211,8 +213,7 @@ void aoe_ktstop(struct ktstate *k);
int aoedev_init(void); int aoedev_init(void);
void aoedev_exit(void); void aoedev_exit(void);
struct aoedev *aoedev_by_aoeaddr(int maj, int min); struct aoedev *aoedev_by_aoeaddr(ulong maj, int min, int do_alloc);
struct aoedev *aoedev_by_sysminor_m(ulong sysminor);
void aoedev_downdev(struct aoedev *d); void aoedev_downdev(struct aoedev *d);
int aoedev_flush(const char __user *str, size_t size); int aoedev_flush(const char __user *str, size_t size);
void aoe_failbuf(struct aoedev *, struct buf *); void aoe_failbuf(struct aoedev *, struct buf *);
...@@ -223,4 +224,3 @@ void aoenet_exit(void); ...@@ -223,4 +224,3 @@ void aoenet_exit(void);
void aoenet_xmit(struct sk_buff_head *); void aoenet_xmit(struct sk_buff_head *);
int is_aoe_netif(struct net_device *ifp); int is_aoe_netif(struct net_device *ifp);
int set_aoe_iflist(const char __user *str, size_t size); int set_aoe_iflist(const char __user *str, size_t size);
...@@ -249,7 +249,7 @@ aoeblk_gdalloc(void *vp) ...@@ -249,7 +249,7 @@ aoeblk_gdalloc(void *vp)
q->queuedata = d; q->queuedata = d;
d->gd = gd; d->gd = gd;
gd->major = AOE_MAJOR; gd->major = AOE_MAJOR;
gd->first_minor = d->sysminor * AOE_PARTITIONS; gd->first_minor = d->sysminor;
gd->fops = &aoe_bdops; gd->fops = &aoe_bdops;
gd->private_data = d; gd->private_data = d;
set_capacity(gd, d->ssize); set_capacity(gd, d->ssize);
......
...@@ -91,7 +91,7 @@ revalidate(const char __user *str, size_t size) ...@@ -91,7 +91,7 @@ revalidate(const char __user *str, size_t size)
pr_err("aoe: invalid device specification %s\n", buf); pr_err("aoe: invalid device specification %s\n", buf);
return -EINVAL; return -EINVAL;
} }
d = aoedev_by_aoeaddr(major, minor); d = aoedev_by_aoeaddr(major, minor, 0);
if (!d) if (!d)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&d->lock, flags); spin_lock_irqsave(&d->lock, flags);
......
...@@ -1149,7 +1149,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) ...@@ -1149,7 +1149,7 @@ aoecmd_ata_rsp(struct sk_buff *skb)
h = (struct aoe_hdr *) skb->data; h = (struct aoe_hdr *) skb->data;
aoemajor = be16_to_cpu(get_unaligned(&h->major)); aoemajor = be16_to_cpu(get_unaligned(&h->major));
d = aoedev_by_aoeaddr(aoemajor, h->minor); d = aoedev_by_aoeaddr(aoemajor, h->minor, 0);
if (d == NULL) { if (d == NULL) {
snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response " snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
"for unknown device %d.%d\n", "for unknown device %d.%d\n",
...@@ -1330,7 +1330,7 @@ aoecmd_cfg_rsp(struct sk_buff *skb) ...@@ -1330,7 +1330,7 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
struct aoe_hdr *h; struct aoe_hdr *h;
struct aoe_cfghdr *ch; struct aoe_cfghdr *ch;
struct aoetgt *t; struct aoetgt *t;
ulong flags, sysminor, aoemajor; ulong flags, aoemajor;
struct sk_buff *sl; struct sk_buff *sl;
struct sk_buff_head queue; struct sk_buff_head queue;
u16 n; u16 n;
...@@ -1349,18 +1349,15 @@ aoecmd_cfg_rsp(struct sk_buff *skb) ...@@ -1349,18 +1349,15 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
"Check shelf dip switches.\n"); "Check shelf dip switches.\n");
return; return;
} }
if (h->minor >= NPERSHELF) { if (aoemajor > AOE_MAXSHELF) {
pr_err("aoe: e%ld.%d %s, %d\n", pr_info("aoe: e%ld.%d: shelf number too large\n",
aoemajor, h->minor, aoemajor, (int) h->minor);
"slot number larger than the maximum",
NPERSHELF-1);
return; return;
} }
sysminor = SYSMINOR(aoemajor, h->minor); d = aoedev_by_aoeaddr(aoemajor, h->minor, 1);
if (sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK) { if (d == NULL) {
printk(KERN_INFO "aoe: e%ld.%d: minor number too large\n", pr_info("aoe: device allocation failure\n");
aoemajor, (int) h->minor);
return; return;
} }
...@@ -1368,12 +1365,6 @@ aoecmd_cfg_rsp(struct sk_buff *skb) ...@@ -1368,12 +1365,6 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
if (n > aoe_maxout) /* keep it reasonable */ if (n > aoe_maxout) /* keep it reasonable */
n = aoe_maxout; n = aoe_maxout;
d = aoedev_by_sysminor_m(sysminor);
if (d == NULL) {
printk(KERN_INFO "aoe: device sysminor_m failure\n");
return;
}
spin_lock_irqsave(&d->lock, flags); spin_lock_irqsave(&d->lock, flags);
t = gettgt(d, h->src); t = gettgt(d, h->src);
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/kdev_t.h>
#include "aoe.h" #include "aoe.h"
static void dummy_timer(ulong); static void dummy_timer(ulong);
...@@ -19,35 +21,63 @@ static void skbpoolfree(struct aoedev *d); ...@@ -19,35 +21,63 @@ static void skbpoolfree(struct aoedev *d);
static struct aoedev *devlist; static struct aoedev *devlist;
static DEFINE_SPINLOCK(devlist_lock); static DEFINE_SPINLOCK(devlist_lock);
/* /* Because some systems will have one, many, or no
* Users who grab a pointer to the device with aoedev_by_aoeaddr or * - partitions,
* aoedev_by_sysminor_m automatically get a reference count and must * - slots per shelf,
* be responsible for performing a aoedev_put. With the addition of * - or shelves,
* async kthread processing I'm no longer confident that we can * we need some flexibility in the way the minor numbers
* guarantee consistency in the face of device flushes. * are allocated. So they are dynamic.
*
* 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.
*/ */
struct aoedev * #define N_DEVS ((1U<<MINORBITS)/AOE_PARTITIONS)
aoedev_by_aoeaddr(int maj, int min)
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 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) minor /= AOE_PARTITIONS;
if (d->aoemajor == maj && d->aoeminor == min) { BUG_ON(minor >= N_DEVS);
d->ref++;
break;
}
spin_unlock_irqrestore(&devlist_lock, flags); spin_lock_irqsave(&used_minors_lock, flags);
return d; 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 void
aoedev_put(struct aoedev *d) aoedev_put(struct aoedev *d)
{ {
...@@ -159,6 +189,7 @@ aoedev_freedev(struct aoedev *d) ...@@ -159,6 +189,7 @@ aoedev_freedev(struct aoedev *d)
if (d->bufpool) if (d->bufpool)
mempool_destroy(d->bufpool); mempool_destroy(d->bufpool);
skbpoolfree(d); skbpoolfree(d);
minor_free(d->sysminor);
kfree(d); kfree(d);
} }
...@@ -246,22 +277,23 @@ skbpoolfree(struct aoedev *d) ...@@ -246,22 +277,23 @@ skbpoolfree(struct aoedev *d)
__skb_queue_head_init(&d->skbpool); __skb_queue_head_init(&d->skbpool);
} }
/* find it or malloc it */ /* find it or allocate it */
struct aoedev * struct aoedev *
aoedev_by_sysminor_m(ulong sysminor) aoedev_by_aoeaddr(ulong maj, int min, int do_alloc)
{ {
struct aoedev *d; struct aoedev *d;
int i; int i;
ulong flags; ulong flags;
ulong sysminor;
spin_lock_irqsave(&devlist_lock, flags); spin_lock_irqsave(&devlist_lock, flags);
for (d=devlist; d; d=d->next) for (d=devlist; d; d=d->next)
if (d->sysminor == sysminor) { if (d->aoemajor == maj && d->aoeminor == min) {
d->ref++; d->ref++;
break; break;
} }
if (d) if (d || !do_alloc || minor_get(&sysminor) < 0)
goto out; goto out;
d = kcalloc(1, sizeof *d, GFP_ATOMIC); d = kcalloc(1, sizeof *d, GFP_ATOMIC);
if (!d) if (!d)
...@@ -280,8 +312,8 @@ aoedev_by_sysminor_m(ulong sysminor) ...@@ -280,8 +312,8 @@ aoedev_by_sysminor_m(ulong sysminor)
for (i = 0; i < NFACTIVE; i++) for (i = 0; i < NFACTIVE; i++)
INIT_LIST_HEAD(&d->factive[i]); INIT_LIST_HEAD(&d->factive[i]);
d->sysminor = sysminor; d->sysminor = sysminor;
d->aoemajor = AOEMAJOR(sysminor); d->aoemajor = maj;
d->aoeminor = AOEMINOR(sysminor); d->aoeminor = min;
d->mintimer = MINTIMER; d->mintimer = MINTIMER;
d->next = devlist; d->next = devlist;
devlist = d; devlist = d;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment