]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 8 Feb 2008 17:42:46 +0000 (09:42 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 8 Feb 2008 17:42:46 +0000 (09:42 -0800)
* 'for-linus' of git://git.kernel.dk/linux-2.6-block:
  Enhanced partition statistics: documentation update
  Enhanced partition statistics: remove old partition statistics
  Enhanced partition statistics: procfs
  Enhanced partition statistics: sysfs
  Enhanced partition statistics: aoe fix
  Enhanced partition statistics: update partition statitics
  Enhanced partition statistics: core statistics
  block: fixup rq_init() a bit

Manually fixed conflict in drivers/block/aoe/aoecmd.c due to statistics
support.

1  2 
drivers/block/aoe/aoecmd.c

index 44beb17e8090cbd1bf20953e0e1f604a1d5e3e06,9e5a37fb36cffa6babded1882f69bd7a21537a4f..d00293ba3b456187a21b1b2aa6fa5ccff49c0dfc
@@@ -1,4 -1,4 +1,4 @@@
 -/* Copyright (c) 2006 Coraid, Inc.  See COPYING for GPL terms. */
 +/* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
  /*
   * aoecmd.c
   * Filesystem request handling methods
@@@ -9,21 -9,19 +9,21 @@@
  #include <linux/skbuff.h>
  #include <linux/netdevice.h>
  #include <linux/genhd.h>
 +#include <linux/moduleparam.h>
  #include <net/net_namespace.h>
  #include <asm/unaligned.h>
  #include "aoe.h"
  
 -#define TIMERTICK (HZ / 10)
 -#define MINTIMER (2 * TIMERTICK)
 -#define MAXTIMER (HZ << 1)
 -
  static int aoe_deadsecs = 60 * 3;
  module_param(aoe_deadsecs, int, 0644);
  MODULE_PARM_DESC(aoe_deadsecs, "After aoe_deadsecs seconds, give up and fail dev.");
  
 -struct sk_buff *
 +static int aoe_maxout = 16;
 +module_param(aoe_maxout, int, 0644);
 +MODULE_PARM_DESC(aoe_maxout,
 +      "Only aoe_maxout outstanding packets for every MAC on eX.Y.");
 +
 +static struct sk_buff *
  new_skb(ulong len)
  {
        struct sk_buff *skb;
  }
  
  static struct frame *
 -getframe(struct aoedev *d, int tag)
 +getframe(struct aoetgt *t, int tag)
  {
        struct frame *f, *e;
  
 -      f = d->frames;
 -      e = f + d->nframes;
 +      f = t->frames;
 +      e = f + t->nframes;
        for (; f<e; f++)
                if (f->tag == tag)
                        return f;
   * This driver reserves tag -1 to mean "unused frame."
   */
  static int
 -newtag(struct aoedev *d)
 +newtag(struct aoetgt *t)
  {
        register ulong n;
  
        n = jiffies & 0xffff;
 -      return n |= (++d->lasttag & 0x7fff) << 16;
 +      return n |= (++t->lasttag & 0x7fff) << 16;
  }
  
  static int
 -aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
 +aoehdr_atainit(struct aoedev *d, struct aoetgt *t, struct aoe_hdr *h)
  {
 -      u32 host_tag = newtag(d);
 +      u32 host_tag = newtag(t);
  
 -      memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
 -      memcpy(h->dst, d->addr, sizeof h->dst);
 +      memcpy(h->src, t->ifp->nd->dev_addr, sizeof h->src);
 +      memcpy(h->dst, t->addr, sizeof h->dst);
        h->type = __constant_cpu_to_be16(ETH_P_AOE);
        h->verfl = AOE_HVER;
        h->major = cpu_to_be16(d->aoemajor);
@@@ -100,162 -98,42 +100,162 @@@ put_lba(struct aoe_atahdr *ah, sector_
  }
  
  static void
 -aoecmd_ata_rw(struct aoedev *d, struct frame *f)
 +ifrotate(struct aoetgt *t)
 +{
 +      t->ifp++;
 +      if (t->ifp >= &t->ifs[NAOEIFS] || t->ifp->nd == NULL)
 +              t->ifp = t->ifs;
 +      if (t->ifp->nd == NULL) {
 +              printk(KERN_INFO "aoe: no interface to rotate to\n");
 +              BUG();
 +      }
 +}
 +
 +static void
 +skb_pool_put(struct aoedev *d, struct sk_buff *skb)
 +{
 +      if (!d->skbpool_hd)
 +              d->skbpool_hd = skb;
 +      else
 +              d->skbpool_tl->next = skb;
 +      d->skbpool_tl = skb;
 +}
 +
 +static struct sk_buff *
 +skb_pool_get(struct aoedev *d)
 +{
 +      struct sk_buff *skb;
 +
 +      skb = d->skbpool_hd;
 +      if (skb && atomic_read(&skb_shinfo(skb)->dataref) == 1) {
 +              d->skbpool_hd = skb->next;
 +              skb->next = NULL;
 +              return skb;
 +      }
 +      if (d->nskbpool < NSKBPOOLMAX
 +      && (skb = new_skb(ETH_ZLEN))) {
 +              d->nskbpool++;
 +              return skb;
 +      }
 +      return NULL;
 +}
 +
 +/* freeframe is where we do our load balancing so it's a little hairy. */
 +static struct frame *
 +freeframe(struct aoedev *d)
 +{
 +      struct frame *f, *e, *rf;
 +      struct aoetgt **t;
 +      struct sk_buff *skb;
 +
 +      if (d->targets[0] == NULL) {    /* shouldn't happen, but I'm paranoid */
 +              printk(KERN_ERR "aoe: NULL TARGETS!\n");
 +              return NULL;
 +      }
 +      t = d->tgt;
 +      t++;
 +      if (t >= &d->targets[NTARGETS] || !*t)
 +              t = d->targets;
 +      for (;;) {
 +              if ((*t)->nout < (*t)->maxout
 +              && t != d->htgt
 +              && (*t)->ifp->nd) {
 +                      rf = NULL;
 +                      f = (*t)->frames;
 +                      e = f + (*t)->nframes;
 +                      for (; f < e; f++) {
 +                              if (f->tag != FREETAG)
 +                                      continue;
 +                              skb = f->skb;
 +                              if (!skb
 +                              && !(f->skb = skb = new_skb(ETH_ZLEN)))
 +                                      continue;
 +                              if (atomic_read(&skb_shinfo(skb)->dataref)
 +                                      != 1) {
 +                                      if (!rf)
 +                                              rf = f;
 +                                      continue;
 +                              }
 +gotone:                               skb_shinfo(skb)->nr_frags = skb->data_len = 0;
 +                              skb_trim(skb, 0);
 +                              d->tgt = t;
 +                              ifrotate(*t);
 +                              return f;
 +                      }
 +                      /* Work can be done, but the network layer is
 +                         holding our precious packets.  Try to grab
 +                         one from the pool. */
 +                      f = rf;
 +                      if (f == NULL) {        /* more paranoia */
 +                              printk(KERN_ERR
 +                                      "aoe: freeframe: %s.\n",
 +                                      "unexpected null rf");
 +                              d->flags |= DEVFL_KICKME;
 +                              return NULL;
 +                      }
 +                      skb = skb_pool_get(d);
 +                      if (skb) {
 +                              skb_pool_put(d, f->skb);
 +                              f->skb = skb;
 +                              goto gotone;
 +                      }
 +                      (*t)->dataref++;
 +                      if ((*t)->nout == 0)
 +                              d->flags |= DEVFL_KICKME;
 +              }
 +              if (t == d->tgt)        /* we've looped and found nada */
 +                      break;
 +              t++;
 +              if (t >= &d->targets[NTARGETS] || !*t)
 +                      t = d->targets;
 +      }
 +      return NULL;
 +}
 +
 +static int
 +aoecmd_ata_rw(struct aoedev *d)
  {
 +      struct frame *f;
        struct aoe_hdr *h;
        struct aoe_atahdr *ah;
        struct buf *buf;
 +      struct bio_vec *bv;
 +      struct aoetgt *t;
        struct sk_buff *skb;
        ulong bcnt;
 -      register sector_t sector;
        char writebit, extbit;
  
        writebit = 0x10;
        extbit = 0x4;
  
 +      f = freeframe(d);
 +      if (f == NULL)
 +              return 0;
 +      t = *d->tgt;
        buf = d->inprocess;
 -
 -      sector = buf->sector;
 -      bcnt = buf->bv_resid;
 -      if (bcnt > d->maxbcnt)
 -              bcnt = d->maxbcnt;
 -
 +      bv = buf->bv;
 +      bcnt = t->ifp->maxbcnt;
 +      if (bcnt == 0)
 +              bcnt = DEFAULTBCNT;
 +      if (bcnt > buf->bv_resid)
 +              bcnt = buf->bv_resid;
        /* initialize the headers & frame */
        skb = f->skb;
        h = (struct aoe_hdr *) skb_mac_header(skb);
        ah = (struct aoe_atahdr *) (h+1);
        skb_put(skb, sizeof *h + sizeof *ah);
        memset(h, 0, skb->len);
 -      f->tag = aoehdr_atainit(d, h);
 +      f->tag = aoehdr_atainit(d, t, h);
 +      t->nout++;
        f->waited = 0;
        f->buf = buf;
 -      f->bufaddr = buf->bufaddr;
 +      f->bufaddr = page_address(bv->bv_page) + buf->bv_off;
        f->bcnt = bcnt;
 -      f->lba = sector;
 +      f->lba = buf->sector;
  
        /* set up ata header */
        ah->scnt = bcnt >> 9;
 -      put_lba(ah, sector);
 +      put_lba(ah, buf->sector);
        if (d->flags & DEVFL_EXT) {
                ah->aflags |= AOEAFL_EXT;
        } else {
                ah->lba3 &= 0x0f;
                ah->lba3 |= 0xe0;       /* LBA bit + obsolete 0xa0 */
        }
 -
        if (bio_data_dir(buf->bio) == WRITE) {
 -              skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
 -                      offset_in_page(f->bufaddr), bcnt);
 +              skb_fill_page_desc(skb, 0, bv->bv_page, buf->bv_off, bcnt);
                ah->aflags |= AOEAFL_WRITE;
                skb->len += bcnt;
                skb->data_len = bcnt;
 +              t->wpkts++;
        } else {
 +              t->rpkts++;
                writebit = 0;
        }
  
  
        /* mark all tracking fields and load out */
        buf->nframesout += 1;
 -      buf->bufaddr += bcnt;
 +      buf->bv_off += bcnt;
        buf->bv_resid -= bcnt;
 -/* printk(KERN_DEBUG "aoe: bv_resid=%ld\n", buf->bv_resid); */
        buf->resid -= bcnt;
        buf->sector += bcnt >> 9;
        if (buf->resid == 0) {
                d->inprocess = NULL;
        } else if (buf->bv_resid == 0) {
 -              buf->bv++;
 -              WARN_ON(buf->bv->bv_len == 0);
 -              buf->bv_resid = buf->bv->bv_len;
 -              buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
 +              buf->bv = ++bv;
 +              buf->bv_resid = bv->bv_len;
 +              WARN_ON(buf->bv_resid == 0);
 +              buf->bv_off = bv->bv_offset;
        }
  
 -      skb->dev = d->ifp;
 +      skb->dev = t->ifp->nd;
        skb = skb_clone(skb, GFP_ATOMIC);
 -      if (skb == NULL)
 -              return;
 -      if (d->sendq_hd)
 -              d->sendq_tl->next = skb;
 -      else
 -              d->sendq_hd = skb;
 -      d->sendq_tl = skb;
 +      if (skb) {
 +              if (d->sendq_hd)
 +                      d->sendq_tl->next = skb;
 +              else
 +                      d->sendq_hd = skb;
 +              d->sendq_tl = skb;
 +      }
 +      return 1;
  }
  
  /* some callers cannot sleep, and they can call this function,
@@@ -354,8 -232,62 +354,8 @@@ cont
        return sl;
  }
  
 -static struct frame *
 -freeframe(struct aoedev *d)
 -{
 -      struct frame *f, *e;
 -      int n = 0;
 -
 -      f = d->frames;
 -      e = f + d->nframes;
 -      for (; f<e; f++) {
 -              if (f->tag != FREETAG)
 -                      continue;
 -              if (atomic_read(&skb_shinfo(f->skb)->dataref) == 1) {
 -                      skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
 -                      skb_trim(f->skb, 0);
 -                      return f;
 -              }
 -              n++;
 -      }
 -      if (n == d->nframes)    /* wait for network layer */
 -              d->flags |= DEVFL_KICKME;
 -
 -      return NULL;
 -}
 -
 -/* 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 = freeframe(d);
 -      if (f == NULL)
 -              return;
 -      if (d->inprocess == NULL) {
 -              if (list_empty(&d->bufq))
 -                      return;
 -              buf = container_of(d->bufq.next, struct buf, bufs);
 -              list_del(d->bufq.next);
 -/*printk(KERN_DEBUG "aoe: bi_size=%ld\n", buf->bio->bi_size); */
 -              d->inprocess = buf;
 -      }
 -      aoecmd_ata_rw(d, f);
 -      goto loop;
 -}
 -
  static void
 -rexmit(struct aoedev *d, struct frame *f)
 +resend(struct aoedev *d, struct aoetgt *t, struct frame *f)
  {
        struct sk_buff *skb;
        struct aoe_hdr *h;
        char buf[128];
        u32 n;
  
 -      n = newtag(d);
 +      ifrotate(t);
 +      n = newtag(t);
 +      skb = f->skb;
 +      h = (struct aoe_hdr *) skb_mac_header(skb);
 +      ah = (struct aoe_atahdr *) (h+1);
  
        snprintf(buf, sizeof buf,
 -              "%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
 -              "retransmit",
 -              d->aoemajor, d->aoeminor, f->tag, jiffies, n);
 +              "%15s e%ld.%d oldtag=%08x@%08lx newtag=%08x "
 +              "s=%012llx d=%012llx nout=%d\n",
 +              "retransmit", d->aoemajor, d->aoeminor, f->tag, jiffies, n,
 +              mac_addr(h->src),
 +              mac_addr(h->dst), t->nout);
        aoechr_error(buf);
  
 -      skb = f->skb;
 -      h = (struct aoe_hdr *) skb_mac_header(skb);
 -      ah = (struct aoe_atahdr *) (h+1);
        f->tag = n;
        h->tag = cpu_to_be32(n);
 -      memcpy(h->dst, d->addr, sizeof h->dst);
 -      memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
 -
 -      n = DEFAULTBCNT / 512;
 -      if (ah->scnt > n) {
 -              ah->scnt = n;
 +      memcpy(h->dst, t->addr, sizeof h->dst);
 +      memcpy(h->src, t->ifp->nd->dev_addr, sizeof h->src);
 +
 +      switch (ah->cmdstat) {
 +      default:
 +              break;
 +      case WIN_READ:
 +      case WIN_READ_EXT:
 +      case WIN_WRITE:
 +      case WIN_WRITE_EXT:
 +              put_lba(ah, f->lba);
 +
 +              n = f->bcnt;
 +              if (n > DEFAULTBCNT)
 +                      n = DEFAULTBCNT;
 +              ah->scnt = n >> 9;
                if (ah->aflags & AOEAFL_WRITE) {
                        skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
 -                              offset_in_page(f->bufaddr), DEFAULTBCNT);
 -                      skb->len = sizeof *h + sizeof *ah + DEFAULTBCNT;
 -                      skb->data_len = DEFAULTBCNT;
 -              }
 -              if (++d->lostjumbo > (d->nframes << 1))
 -              if (d->maxbcnt != DEFAULTBCNT) {
 -                      printk(KERN_INFO "aoe: e%ld.%ld: too many lost jumbo on %s - using 1KB frames.\n",
 -                              d->aoemajor, d->aoeminor, d->ifp->name);
 -                      d->maxbcnt = DEFAULTBCNT;
 -                      d->flags |= DEVFL_MAXBCNT;
 +                              offset_in_page(f->bufaddr), n);
 +                      skb->len = sizeof *h + sizeof *ah + n;
 +                      skb->data_len = n;
                }
        }
 -
 -      skb->dev = d->ifp;
 +      skb->dev = t->ifp->nd;
        skb = skb_clone(skb, GFP_ATOMIC);
        if (skb == NULL)
                return;
@@@ -425,92 -352,10 +425,92 @@@ tsince(int tag
        return n;
  }
  
 +static struct aoeif *
 +getif(struct aoetgt *t, struct net_device *nd)
 +{
 +      struct aoeif *p, *e;
 +
 +      p = t->ifs;
 +      e = p + NAOEIFS;
 +      for (; p < e; p++)
 +              if (p->nd == nd)
 +                      return p;
 +      return NULL;
 +}
 +
 +static struct aoeif *
 +addif(struct aoetgt *t, struct net_device *nd)
 +{
 +      struct aoeif *p;
 +
 +      p = getif(t, NULL);
 +      if (!p)
 +              return NULL;
 +      p->nd = nd;
 +      p->maxbcnt = DEFAULTBCNT;
 +      p->lost = 0;
 +      p->lostjumbo = 0;
 +      return p;
 +}
 +
 +static void
 +ejectif(struct aoetgt *t, struct aoeif *ifp)
 +{
 +      struct aoeif *e;
 +      ulong n;
 +
 +      e = t->ifs + NAOEIFS - 1;
 +      n = (e - ifp) * sizeof *ifp;
 +      memmove(ifp, ifp+1, n);
 +      e->nd = NULL;
 +}
 +
 +static int
 +sthtith(struct aoedev *d)
 +{
 +      struct frame *f, *e, *nf;
 +      struct sk_buff *skb;
 +      struct aoetgt *ht = *d->htgt;
 +
 +      f = ht->frames;
 +      e = f + ht->nframes;
 +      for (; f < e; f++) {
 +              if (f->tag == FREETAG)
 +                      continue;
 +              nf = freeframe(d);
 +              if (!nf)
 +                      return 0;
 +              skb = nf->skb;
 +              *nf = *f;
 +              f->skb = skb;
 +              f->tag = FREETAG;
 +              nf->waited = 0;
 +              ht->nout--;
 +              (*d->tgt)->nout++;
 +              resend(d, *d->tgt, nf);
 +      }
 +      /* he's clean, he's useless.  take away his interfaces */
 +      memset(ht->ifs, 0, sizeof ht->ifs);
 +      d->htgt = NULL;
 +      return 1;
 +}
 +
 +static inline unsigned char
 +ata_scnt(unsigned char *packet) {
 +      struct aoe_hdr *h;
 +      struct aoe_atahdr *ah;
 +
 +      h = (struct aoe_hdr *) packet;
 +      ah = (struct aoe_atahdr *) (h+1);
 +      return ah->scnt;
 +}
 +
  static void
  rexmit_timer(ulong vp)
  {
        struct aoedev *d;
 +      struct aoetgt *t, **tt, **te;
 +      struct aoeif *ifp;
        struct frame *f, *e;
        struct sk_buff *sl;
        register long timeout;
                spin_unlock_irqrestore(&d->lock, flags);
                return;
        }
 -      f = d->frames;
 -      e = f + d->nframes;
 -      for (; f<e; f++) {
 -              if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
 +      tt = d->targets;
 +      te = tt + NTARGETS;
 +      for (; tt < te && *tt; tt++) {
 +              t = *tt;
 +              f = t->frames;
 +              e = f + t->nframes;
 +              for (; f < e; f++) {
 +                      if (f->tag == FREETAG
 +                      || tsince(f->tag) < timeout)
 +                              continue;
                        n = f->waited += timeout;
                        n /= HZ;
 -                      if (n > aoe_deadsecs) { /* waited too long for response */
 +                      if (n > aoe_deadsecs) {
 +                              /* waited too long.  device failure. */
                                aoedev_downdev(d);
                                break;
                        }
 -                      rexmit(d, f);
 +
 +                      if (n > HELPWAIT /* see if another target can help */
 +                      && (tt != d->targets || d->targets[1]))
 +                              d->htgt = tt;
 +
 +                      if (t->nout == t->maxout) {
 +                              if (t->maxout > 1)
 +                                      t->maxout--;
 +                              t->lastwadj = jiffies;
 +                      }
 +
 +                      ifp = getif(t, f->skb->dev);
 +                      if (ifp && ++ifp->lost > (t->nframes << 1)
 +                      && (ifp != t->ifs || t->ifs[1].nd)) {
 +                              ejectif(t, ifp);
 +                              ifp = NULL;
 +                      }
 +
 +                      if (ata_scnt(skb_mac_header(f->skb)) > DEFAULTBCNT / 512
 +                      && ifp && ++ifp->lostjumbo > (t->nframes << 1)
 +                      && ifp->maxbcnt != DEFAULTBCNT) {
 +                              printk(KERN_INFO
 +                                      "aoe: e%ld.%d: "
 +                                      "too many lost jumbo on "
 +                                      "%s:%012llx - "
 +                                      "falling back to %d frames.\n",
 +                                      d->aoemajor, d->aoeminor,
 +                                      ifp->nd->name, mac_addr(t->addr),
 +                                      DEFAULTBCNT);
 +                              ifp->maxbcnt = 0;
 +                      }
 +                      resend(d, t, f);
 +              }
 +
 +              /* window check */
 +              if (t->nout == t->maxout
 +              && t->maxout < t->nframes
 +              && (jiffies - t->lastwadj)/HZ > 10) {
 +                      t->maxout++;
 +                      t->lastwadj = jiffies;
                }
        }
 -      if (d->flags & DEVFL_KICKME) {
 +
 +      if (d->sendq_hd) {
 +              n = d->rttavg <<= 1;
 +              if (n > MAXTIMER)
 +                      d->rttavg = MAXTIMER;
 +      }
 +
 +      if (d->flags & DEVFL_KICKME || d->htgt) {
                d->flags &= ~DEVFL_KICKME;
                aoecmd_work(d);
        }
  
        sl = d->sendq_hd;
        d->sendq_hd = d->sendq_tl = NULL;
 -      if (sl) {
 -              n = d->rttavg <<= 1;
 -              if (n > MAXTIMER)
 -                      d->rttavg = MAXTIMER;
 -      }
  
        d->timer.expires = jiffies + TIMERTICK;
        add_timer(&d->timer);
        aoenet_xmit(sl);
  }
  
 +/* enters with d->lock held */
 +void
 +aoecmd_work(struct aoedev *d)
 +{
 +      struct buf *buf;
 +loop:
 +      if (d->htgt && !sthtith(d))
 +              return;
 +      if (d->inprocess == NULL) {
 +              if (list_empty(&d->bufq))
 +                      return;
 +              buf = container_of(d->bufq.next, struct buf, bufs);
 +              list_del(d->bufq.next);
 +              d->inprocess = buf;
 +      }
 +      if (aoecmd_ata_rw(d))
 +              goto loop;
 +}
 +
  /* this function performs work that has been deferred until sleeping is OK
   */
  void
@@@ -662,7 -440,7 +662,7 @@@ aoecmd_sleepwork(struct work_struct *wo
  }
  
  static void
 -ataid_complete(struct aoedev *d, unsigned char *id)
 +ataid_complete(struct aoedev *d, struct aoetgt *t, unsigned char *id)
  {
        u64 ssize;
        u16 n;
        }
  
        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),
 +              printk(KERN_INFO
 +                      "aoe: %012llx e%ld.%d v%04x has %llu sectors\n",
 +                      mac_addr(t->addr),
                        d->aoemajor, d->aoeminor,
                        d->fw_ver, (long long)ssize);
        d->ssize = ssize;
        d->geo.start = 0;
 +      if (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
 +              return;
        if (d->gd != NULL) {
                d->gd->capacity = ssize;
                d->flags |= DEVFL_NEWSIZE;
 -      } else {
 -              if (d->flags & DEVFL_GDALLOC) {
 -                      printk(KERN_ERR "aoe: can't schedule work for e%lu.%lu, %s\n",
 -                             d->aoemajor, d->aoeminor,
 -                             "it's already on!  This shouldn't happen.\n");
 -                      return;
 -              }
 +      } else
                d->flags |= DEVFL_GDALLOC;
 -      }
        schedule_work(&d->work);
  }
  
@@@ -737,31 -519,6 +737,31 @@@ calc_rttavg(struct aoedev *d, int rtt
        d->rttavg += n >> 2;
  }
  
- diskstats(struct gendisk *disk, struct bio *bio, ulong duration)
 +static struct aoetgt *
 +gettgt(struct aoedev *d, char *addr)
 +{
 +      struct aoetgt **t, **e;
 +
 +      t = d->targets;
 +      e = t + NTARGETS;
 +      for (; t < e && *t; t++)
 +              if (memcmp((*t)->addr, addr, sizeof((*t)->addr)) == 0)
 +                      return *t;
 +      return NULL;
 +}
 +
 +static inline void
-       disk_stat_inc(disk, ios[rw]);
-       disk_stat_add(disk, ticks[rw], duration);
-       disk_stat_add(disk, sectors[rw], n_sect);
-       disk_stat_add(disk, io_ticks, duration);
++diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector)
 +{
 +      unsigned long n_sect = bio->bi_size >> 9;
 +      const int rw = bio_data_dir(bio);
 +
++      all_stat_inc(disk, ios[rw], sector);
++      all_stat_add(disk, ticks[rw], duration, sector);
++      all_stat_add(disk, sectors[rw], n_sect, sector);
++      all_stat_add(disk, io_ticks, duration, sector);
 +}
 +
  void
  aoecmd_ata_rsp(struct sk_buff *skb)
  {
        struct frame *f;
        struct buf *buf;
        struct sk_buff *sl;
 +      struct aoetgt *t;
 +      struct aoeif *ifp;
        register long n;
        ulong flags;
        char ebuf[128];
        spin_lock_irqsave(&d->lock, flags);
  
        n = be32_to_cpu(get_unaligned(&hin->tag));
 -      f = getframe(d, n);
 +      t = gettgt(d, hin->src);
 +      if (t == NULL) {
 +              printk(KERN_INFO "aoe: can't find target e%ld.%d:%012llx\n",
 +                      d->aoemajor, d->aoeminor, mac_addr(hin->src));
 +              spin_unlock_irqrestore(&d->lock, flags);
 +              return;
 +      }
 +      f = getframe(t, n);
        if (f == NULL) {
                calc_rttavg(d, -tsince(n));
                spin_unlock_irqrestore(&d->lock, flags);
        ahout = (struct aoe_atahdr *) (hout+1);
        buf = f->buf;
  
 -      if (ahout->cmdstat == WIN_IDENTIFY)
 -              d->flags &= ~DEVFL_PAUSE;
        if (ahin->cmdstat & 0xa9) {     /* these bits cleared on success */
                printk(KERN_ERR
 -                      "aoe: ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%ld\n",
 +                      "aoe: ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%d\n",
                        ahout->cmdstat, ahin->cmdstat,
                        d->aoemajor, d->aoeminor);
                if (buf)
                        buf->flags |= BUFFL_FAIL;
        } else {
 +              if (d->htgt && t == *d->htgt) /* I'll help myself, thank you. */
 +                      d->htgt = NULL;
                n = ahout->scnt << 9;
                switch (ahout->cmdstat) {
                case WIN_READ:
                case WIN_READ_EXT:
                        if (skb->len - sizeof *hin - sizeof *ahin < n) {
                                printk(KERN_ERR
 -                                      "aoe: runt data size in read.  skb->len=%d\n",
 -                                      skb->len);
 +                                      "aoe: %s.  skb->len=%d need=%ld\n",
 +                                      "runt data size in read", skb->len, n);
                                /* fail frame f?  just returning will rexmit. */
                                spin_unlock_irqrestore(&d->lock, flags);
                                return;
                        memcpy(f->bufaddr, ahin+1, n);
                case WIN_WRITE:
                case WIN_WRITE_EXT:
 +                      ifp = getif(t, skb->dev);
 +                      if (ifp) {
 +                              ifp->lost = 0;
 +                              if (n > DEFAULTBCNT)
 +                                      ifp->lostjumbo = 0;
 +                      }
                        if (f->bcnt -= n) {
 -                              skb = f->skb;
 +                              f->lba += n >> 9;
                                f->bufaddr += n;
 -                              put_lba(ahout, f->lba += ahout->scnt);
 -                              n = f->bcnt;
 -                              if (n > DEFAULTBCNT)
 -                                      n = DEFAULTBCNT;
 -                              ahout->scnt = n >> 9;
 -                              if (ahout->aflags & AOEAFL_WRITE) {
 -                                      skb_fill_page_desc(skb, 0,
 -                                              virt_to_page(f->bufaddr),
 -                                              offset_in_page(f->bufaddr), n);
 -                                      skb->len = sizeof *hout + sizeof *ahout + n;
 -                                      skb->data_len = n;
 -                              }
 -                              f->tag = newtag(d);
 -                              hout->tag = cpu_to_be32(f->tag);
 -                              skb->dev = d->ifp;
 -                              skb = skb_clone(skb, GFP_ATOMIC);
 -                              spin_unlock_irqrestore(&d->lock, flags);
 -                              if (skb)
 -                                      aoenet_xmit(skb);
 -                              return;
 +                              resend(d, t, f);
 +                              goto xmit;
                        }
 -                      if (n > DEFAULTBCNT)
 -                              d->lostjumbo = 0;
                        break;
                case WIN_IDENTIFY:
                        if (skb->len - sizeof *hin - sizeof *ahin < 512) {
                                spin_unlock_irqrestore(&d->lock, flags);
                                return;
                        }
 -                      ataid_complete(d, (char *) (ahin+1));
 +                      ataid_complete(d, t, (char *) (ahin+1));
                        break;
                default:
                        printk(KERN_INFO
                }
        }
  
 -      if (buf) {
 -              buf->nframesout -= 1;
 -              if (buf->nframesout == 0 && buf->resid == 0) {
 -                      unsigned long duration = jiffies - buf->start_time;
 -                      unsigned long n_sect = buf->bio->bi_size >> 9;
 -                      struct gendisk *disk = d->gd;
 -                      const int rw = bio_data_dir(buf->bio);
 -
 -                      all_stat_inc(disk, ios[rw], buf->sector);
 -                      all_stat_add(disk, ticks[rw], duration, buf->sector);
 -                      all_stat_add(disk, sectors[rw], n_sect, buf->sector);
 -                      all_stat_add(disk, io_ticks, duration, buf->sector);
 -                      n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
 -                      bio_endio(buf->bio, n);
 -                      mempool_free(buf, d->bufpool);
 -              }
 +      if (buf && --buf->nframesout == 0 && buf->resid == 0) {
-               diskstats(d->gd, buf->bio, jiffies - buf->stime);
++              diskstats(d->gd, buf->bio, jiffies - buf->stime, buf->sector);
 +              n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
 +              bio_endio(buf->bio, n);
 +              mempool_free(buf, d->bufpool);
        }
  
        f->buf = NULL;
        f->tag = FREETAG;
 +      t->nout--;
  
        aoecmd_work(d);
 +xmit:
        sl = d->sendq_hd;
        d->sendq_hd = d->sendq_tl = NULL;
  
@@@ -908,20 -679,23 +908,20 @@@ aoecmd_cfg(ushort aoemajor, unsigned ch
        aoenet_xmit(sl);
  }
   
 -/*
 - * Since we only call this in one place (and it only prepares one frame)
 - * we just return the skb.  Usually we'd chain it up to the aoedev sendq.
 - */
 -static struct sk_buff *
 +struct sk_buff *
  aoecmd_ata_id(struct aoedev *d)
  {
        struct aoe_hdr *h;
        struct aoe_atahdr *ah;
        struct frame *f;
        struct sk_buff *skb;
 +      struct aoetgt *t;
  
        f = freeframe(d);
 -      if (f == NULL) {
 -              printk(KERN_ERR "aoe: can't get a frame. This shouldn't happen.\n");
 +      if (f == NULL)
                return NULL;
 -      }
 +
 +      t = *d->tgt;
  
        /* initialize the headers & frame */
        skb = f->skb;
        ah = (struct aoe_atahdr *) (h+1);
        skb_put(skb, sizeof *h + sizeof *ah);
        memset(h, 0, skb->len);
 -      f->tag = aoehdr_atainit(d, h);
 +      f->tag = aoehdr_atainit(d, t, h);
 +      t->nout++;
        f->waited = 0;
  
        /* set up ata header */
        ah->cmdstat = WIN_IDENTIFY;
        ah->lba3 = 0xa0;
  
 -      skb->dev = d->ifp;
 +      skb->dev = t->ifp->nd;
  
        d->rttavg = MAXTIMER;
        d->timer.function = rexmit_timer;
        return skb_clone(skb, GFP_ATOMIC);
  }
   
 +static struct aoetgt *
 +addtgt(struct aoedev *d, char *addr, ulong nframes)
 +{
 +      struct aoetgt *t, **tt, **te;
 +      struct frame *f, *e;
 +
 +      tt = d->targets;
 +      te = tt + NTARGETS;
 +      for (; tt < te && *tt; tt++)
 +              ;
 +
 +      if (tt == te) {
 +              printk(KERN_INFO
 +                      "aoe: device addtgt failure; too many targets\n");
 +              return NULL;
 +      }
 +      t = kcalloc(1, sizeof *t, GFP_ATOMIC);
 +      f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
 +      if (!t || !f) {
 +              kfree(f);
 +              kfree(t);
 +              printk(KERN_INFO "aoe: cannot allocate memory to add target\n");
 +              return NULL;
 +      }
 +
 +      t->nframes = nframes;
 +      t->frames = f;
 +      e = f + nframes;
 +      for (; f < e; f++)
 +              f->tag = FREETAG;
 +      memcpy(t->addr, addr, sizeof t->addr);
 +      t->ifp = t->ifs;
 +      t->maxout = t->nframes;
 +      return *tt = t;
 +}
 +
  void
  aoecmd_cfg_rsp(struct sk_buff *skb)
  {
        struct aoedev *d;
        struct aoe_hdr *h;
        struct aoe_cfghdr *ch;
 +      struct aoetgt *t;
 +      struct aoeif *ifp;
        ulong flags, sysminor, aoemajor;
        struct sk_buff *sl;
 -      enum { MAXFRAMES = 16 };
        u16 n;
  
        h = (struct aoe_hdr *) skb_mac_header(skb);
        }
  
        n = be16_to_cpu(ch->bufcnt);
 -      if (n > MAXFRAMES)      /* keep it reasonable */
 -              n = MAXFRAMES;
 +      if (n > aoe_maxout)     /* keep it reasonable */
 +              n = aoe_maxout;
  
 -      d = aoedev_by_sysminor_m(sysminor, n);
 +      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);
  
 -      /* permit device to migrate mac and network interface */
 -      d->ifp = skb->dev;
 -      memcpy(d->addr, h->src, sizeof d->addr);
 -      if (!(d->flags & DEVFL_MAXBCNT)) {
 -              n = d->ifp->mtu;
 +      t = gettgt(d, h->src);
 +      if (!t) {
 +              t = addtgt(d, h->src, n);
 +              if (!t) {
 +                      spin_unlock_irqrestore(&d->lock, flags);
 +                      return;
 +              }
 +      }
 +      ifp = getif(t, skb->dev);
 +      if (!ifp) {
 +              ifp = addif(t, skb->dev);
 +              if (!ifp) {
 +                      printk(KERN_INFO
 +                              "aoe: device addif failure; "
 +                              "too many interfaces?\n");
 +                      spin_unlock_irqrestore(&d->lock, flags);
 +                      return;
 +              }
 +      }
 +      if (ifp->maxbcnt) {
 +              n = ifp->nd->mtu;
                n -= sizeof (struct aoe_hdr) + sizeof (struct aoe_atahdr);
                n /= 512;
                if (n > ch->scnt)
                        n = ch->scnt;
                n = n ? n * 512 : DEFAULTBCNT;
 -              if (n != d->maxbcnt) {
 +              if (n != ifp->maxbcnt) {
                        printk(KERN_INFO
 -                              "aoe: e%ld.%ld: setting %d byte data frames on %s\n",
 -                              d->aoemajor, d->aoeminor, n, d->ifp->name);
 -                      d->maxbcnt = n;
 +                              "aoe: e%ld.%d: setting %d%s%s:%012llx\n",
 +                              d->aoemajor, d->aoeminor, n,
 +                              " byte data frames on ", ifp->nd->name,
 +                              mac_addr(t->addr));
 +                      ifp->maxbcnt = n;
                }
        }
  
        /* don't change users' perspective */
 -      if (d->nopen && !(d->flags & DEVFL_PAUSE)) {
 +      if (d->nopen) {
                spin_unlock_irqrestore(&d->lock, flags);
                return;
        }
 -      d->flags |= DEVFL_PAUSE;        /* force pause */
 -      d->mintimer = MINTIMER;
        d->fw_ver = be16_to_cpu(ch->fwver);
  
 -      /* check for already outstanding ataid */
 -      sl = aoedev_isbusy(d) == 0 ? aoecmd_ata_id(d) : NULL;
 +      sl = aoecmd_ata_id(d);
  
        spin_unlock_irqrestore(&d->lock, flags);
  
        aoenet_xmit(sl);
  }
  
 +void
 +aoecmd_cleanslate(struct aoedev *d)
 +{
 +      struct aoetgt **t, **te;
 +      struct aoeif *p, *e;
 +
 +      d->mintimer = MINTIMER;
 +
 +      t = d->targets;
 +      te = t + NTARGETS;
 +      for (; t < te && *t; t++) {
 +              (*t)->maxout = (*t)->nframes;
 +              p = (*t)->ifs;
 +              e = p + NAOEIFS;
 +              for (; p < e; p++) {
 +                      p->lostjumbo = 0;
 +                      p->lost = 0;
 +                      p->maxbcnt = DEFAULTBCNT;
 +              }
 +      }
 +}