|
@@ -8,6 +8,7 @@
|
|
|
#include <linux/blkdev.h>
|
|
|
#include <linux/skbuff.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
+#include <linux/genhd.h>
|
|
|
#include <asm/unaligned.h>
|
|
|
#include "aoe.h"
|
|
|
|
|
@@ -189,12 +190,67 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* some callers cannot sleep, and they can call this function,
|
|
|
+ * transmitting the packets later, when interrupts are on
|
|
|
+ */
|
|
|
+static struct sk_buff *
|
|
|
+aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
|
|
|
+{
|
|
|
+ struct aoe_hdr *h;
|
|
|
+ struct aoe_cfghdr *ch;
|
|
|
+ struct sk_buff *skb, *sl, *sl_tail;
|
|
|
+ struct net_device *ifp;
|
|
|
+
|
|
|
+ sl = sl_tail = NULL;
|
|
|
+
|
|
|
+ read_lock(&dev_base_lock);
|
|
|
+ for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
|
|
|
+ dev_hold(ifp);
|
|
|
+ if (!is_aoe_netif(ifp))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ skb = new_skb(ifp, sizeof *h + sizeof *ch);
|
|
|
+ if (skb == NULL) {
|
|
|
+ printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (sl_tail == NULL)
|
|
|
+ sl_tail = skb;
|
|
|
+ h = (struct aoe_hdr *) skb->mac.raw;
|
|
|
+ memset(h, 0, sizeof *h + sizeof *ch);
|
|
|
+
|
|
|
+ memset(h->dst, 0xff, sizeof h->dst);
|
|
|
+ memcpy(h->src, ifp->dev_addr, sizeof h->src);
|
|
|
+ h->type = __constant_cpu_to_be16(ETH_P_AOE);
|
|
|
+ h->verfl = AOE_HVER;
|
|
|
+ h->major = cpu_to_be16(aoemajor);
|
|
|
+ h->minor = aoeminor;
|
|
|
+ h->cmd = AOECMD_CFG;
|
|
|
+
|
|
|
+ skb->next = sl;
|
|
|
+ sl = skb;
|
|
|
+ }
|
|
|
+ read_unlock(&dev_base_lock);
|
|
|
+
|
|
|
+ if (tail != NULL)
|
|
|
+ *tail = sl_tail;
|
|
|
+ return sl;
|
|
|
+}
|
|
|
+
|
|
|
/* enters with d->lock held */
|
|
|
void
|
|
|
aoecmd_work(struct aoedev *d)
|
|
|
{
|
|
|
struct frame *f;
|
|
|
struct buf *buf;
|
|
|
+
|
|
|
+ if (d->flags & DEVFL_PAUSE) {
|
|
|
+ if (!aoedev_isbusy(d))
|
|
|
+ d->sendq_hd = aoecmd_cfg_pkts(d->aoemajor,
|
|
|
+ d->aoeminor, &d->sendq_tl);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
loop:
|
|
|
f = getframe(d, FREETAG);
|
|
|
if (f == NULL)
|
|
@@ -306,6 +362,37 @@ tdie: spin_unlock_irqrestore(&d->lock, flags);
|
|
|
aoenet_xmit(sl);
|
|
|
}
|
|
|
|
|
|
+/* this function performs work that has been deferred until sleeping is OK
|
|
|
+ */
|
|
|
+void
|
|
|
+aoecmd_sleepwork(void *vp)
|
|
|
+{
|
|
|
+ struct aoedev *d = (struct aoedev *) vp;
|
|
|
+
|
|
|
+ if (d->flags & DEVFL_GDALLOC)
|
|
|
+ aoeblk_gdalloc(d);
|
|
|
+
|
|
|
+ if (d->flags & DEVFL_NEWSIZE) {
|
|
|
+ struct block_device *bd;
|
|
|
+ unsigned long flags;
|
|
|
+ u64 ssize;
|
|
|
+
|
|
|
+ ssize = d->gd->capacity;
|
|
|
+ bd = bdget_disk(d->gd, 0);
|
|
|
+
|
|
|
+ if (bd) {
|
|
|
+ mutex_lock(&bd->bd_inode->i_mutex);
|
|
|
+ i_size_write(bd->bd_inode, (loff_t)ssize<<9);
|
|
|
+ mutex_unlock(&bd->bd_inode->i_mutex);
|
|
|
+ bdput(bd);
|
|
|
+ }
|
|
|
+ spin_lock_irqsave(&d->lock, flags);
|
|
|
+ d->flags |= DEVFL_UP;
|
|
|
+ d->flags &= ~DEVFL_NEWSIZE;
|
|
|
+ spin_unlock_irqrestore(&d->lock, flags);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
ataid_complete(struct aoedev *d, unsigned char *id)
|
|
|
{
|
|
@@ -340,21 +427,29 @@ ataid_complete(struct aoedev *d, unsigned char *id)
|
|
|
d->geo.heads = le16_to_cpu(get_unaligned((__le16 *) &id[55<<1]));
|
|
|
d->geo.sectors = le16_to_cpu(get_unaligned((__le16 *) &id[56<<1]));
|
|
|
}
|
|
|
+
|
|
|
+ if (d->ssize != ssize)
|
|
|
+ printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu "
|
|
|
+ "sectors\n", (unsigned long long)mac_addr(d->addr),
|
|
|
+ d->aoemajor, d->aoeminor,
|
|
|
+ d->fw_ver, (long long)ssize);
|
|
|
d->ssize = ssize;
|
|
|
d->geo.start = 0;
|
|
|
if (d->gd != NULL) {
|
|
|
d->gd->capacity = ssize;
|
|
|
- d->flags |= DEVFL_UP;
|
|
|
- return;
|
|
|
- }
|
|
|
- if (d->flags & DEVFL_WORKON) {
|
|
|
- printk(KERN_INFO "aoe: ataid_complete: can't schedule work, it's already on! "
|
|
|
- "(This really shouldn't happen).\n");
|
|
|
- return;
|
|
|
+ d->flags |= DEVFL_NEWSIZE;
|
|
|
+ } else {
|
|
|
+ if (d->flags & DEVFL_GDALLOC) {
|
|
|
+ printk(KERN_INFO "aoe: %s: %s e%lu.%lu, %s\n",
|
|
|
+ __FUNCTION__,
|
|
|
+ "can't schedule work for",
|
|
|
+ d->aoemajor, d->aoeminor,
|
|
|
+ "it's already on! (This really shouldn't happen).\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ d->flags |= DEVFL_GDALLOC;
|
|
|
}
|
|
|
- INIT_WORK(&d->work, aoeblk_gdalloc, d);
|
|
|
schedule_work(&d->work);
|
|
|
- d->flags |= DEVFL_WORKON;
|
|
|
}
|
|
|
|
|
|
static void
|
|
@@ -452,7 +547,7 @@ aoecmd_ata_rsp(struct sk_buff *skb)
|
|
|
return;
|
|
|
}
|
|
|
ataid_complete(d, (char *) (ahin+1));
|
|
|
- /* d->flags |= DEVFL_WC_UPDATE; */
|
|
|
+ d->flags &= ~DEVFL_PAUSE;
|
|
|
break;
|
|
|
default:
|
|
|
printk(KERN_INFO "aoe: aoecmd_ata_rsp: unrecognized "
|
|
@@ -485,51 +580,19 @@ aoecmd_ata_rsp(struct sk_buff *skb)
|
|
|
f->tag = FREETAG;
|
|
|
|
|
|
aoecmd_work(d);
|
|
|
-
|
|
|
sl = d->sendq_hd;
|
|
|
d->sendq_hd = d->sendq_tl = NULL;
|
|
|
|
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
-
|
|
|
aoenet_xmit(sl);
|
|
|
}
|
|
|
|
|
|
void
|
|
|
aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
|
|
|
{
|
|
|
- struct aoe_hdr *h;
|
|
|
- struct aoe_cfghdr *ch;
|
|
|
- struct sk_buff *skb, *sl;
|
|
|
- struct net_device *ifp;
|
|
|
-
|
|
|
- sl = NULL;
|
|
|
-
|
|
|
- read_lock(&dev_base_lock);
|
|
|
- for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
|
|
|
- dev_hold(ifp);
|
|
|
- if (!is_aoe_netif(ifp))
|
|
|
- continue;
|
|
|
-
|
|
|
- skb = new_skb(ifp, sizeof *h + sizeof *ch);
|
|
|
- if (skb == NULL) {
|
|
|
- printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n");
|
|
|
- continue;
|
|
|
- }
|
|
|
- h = (struct aoe_hdr *) skb->mac.raw;
|
|
|
- memset(h, 0, sizeof *h + sizeof *ch);
|
|
|
-
|
|
|
- memset(h->dst, 0xff, sizeof h->dst);
|
|
|
- memcpy(h->src, ifp->dev_addr, sizeof h->src);
|
|
|
- h->type = __constant_cpu_to_be16(ETH_P_AOE);
|
|
|
- h->verfl = AOE_HVER;
|
|
|
- h->major = cpu_to_be16(aoemajor);
|
|
|
- h->minor = aoeminor;
|
|
|
- h->cmd = AOECMD_CFG;
|
|
|
+ struct sk_buff *sl;
|
|
|
|
|
|
- skb->next = sl;
|
|
|
- sl = skb;
|
|
|
- }
|
|
|
- read_unlock(&dev_base_lock);
|
|
|
+ sl = aoecmd_cfg_pkts(aoemajor, aoeminor, NULL);
|
|
|
|
|
|
aoenet_xmit(sl);
|
|
|
}
|
|
@@ -562,9 +625,6 @@ aoecmd_ata_id(struct aoedev *d)
|
|
|
f->waited = 0;
|
|
|
f->writedatalen = 0;
|
|
|
|
|
|
- /* this message initializes the device, so we reset the rttavg */
|
|
|
- d->rttavg = MAXTIMER;
|
|
|
-
|
|
|
/* set up ata header */
|
|
|
ah->scnt = 1;
|
|
|
ah->cmdstat = WIN_IDENTIFY;
|
|
@@ -572,12 +632,8 @@ aoecmd_ata_id(struct aoedev *d)
|
|
|
|
|
|
skb = skb_prepare(d, f);
|
|
|
|
|
|
- /* we now want to start the rexmit tracking */
|
|
|
- d->flags &= ~DEVFL_TKILL;
|
|
|
- d->timer.data = (ulong) d;
|
|
|
+ d->rttavg = MAXTIMER;
|
|
|
d->timer.function = rexmit_timer;
|
|
|
- d->timer.expires = jiffies + TIMERTICK;
|
|
|
- add_timer(&d->timer);
|
|
|
|
|
|
return skb;
|
|
|
}
|
|
@@ -619,23 +675,28 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
|
|
|
if (bufcnt > MAXFRAMES) /* keep it reasonable */
|
|
|
bufcnt = MAXFRAMES;
|
|
|
|
|
|
- d = aoedev_set(sysminor, h->src, skb->dev, bufcnt);
|
|
|
+ d = aoedev_by_sysminor_m(sysminor, bufcnt);
|
|
|
if (d == NULL) {
|
|
|
- printk(KERN_INFO "aoe: aoecmd_cfg_rsp: device set failure\n");
|
|
|
+ printk(KERN_INFO "aoe: aoecmd_cfg_rsp: device sysminor_m failure\n");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
|
|
|
|
- if (d->flags & (DEVFL_UP | DEVFL_CLOSEWAIT)) {
|
|
|
+ /* permit device to migrate mac and network interface */
|
|
|
+ d->ifp = skb->dev;
|
|
|
+ memcpy(d->addr, h->src, sizeof d->addr);
|
|
|
+
|
|
|
+ /* don't change users' perspective */
|
|
|
+ if (d->nopen && !(d->flags & DEVFL_PAUSE)) {
|
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+ d->flags |= DEVFL_PAUSE; /* force pause */
|
|
|
d->fw_ver = be16_to_cpu(ch->fwver);
|
|
|
|
|
|
- /* we get here only if the device is new */
|
|
|
- sl = aoecmd_ata_id(d);
|
|
|
+ /* check for already outstanding ataid */
|
|
|
+ sl = aoedev_isbusy(d) == 0 ? aoecmd_ata_id(d) : NULL;
|
|
|
|
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
|