NFACTIVE = 61,
 
        TIMERTICK = HZ / 10,
-       MINTIMER = HZ >> 2,
+       RTTSCALE = 8,
+       RTTDSCALE = 3,
        MAXTIMER = HZ << 1,
+       RTTAVG_INIT = HZ / 4 << RTTSCALE,
+       RTTDEV_INIT = RTTAVG_INIT / 4,
 };
 
 struct buf {
        struct list_head ffree;                 /* list of free frames */
        struct aoeif ifs[NAOEIFS];
        struct aoeif *ifp;      /* current aoeif in use */
-       ushort nout;
+       ushort nout;            /* value of nout when skb was sent */
        ushort maxout;          /* current value for max outstanding */
+       ushort next_cwnd;       /* incr maxout after decrementing to zero */
+       ushort ssthresh;        /* slow start threshold */
        ulong falloc;           /* number of allocated frames */
-       ulong lastwadj;         /* last window adjustment */
        int minbcnt;
        int wpkts, rpkts;
 };
        u16 aoeminor;
        u16 flags;
        u16 nopen;              /* (bd_openers isn't available without sleeping) */
-       u16 rttavg;             /* round trip average of requests/responses */
-       u16 mintimer;
+       u16 rttavg;             /* scaled AoE round trip time average */
+       u16 rttdev;             /* scaled round trip time mean deviation */
        u16 fw_ver;             /* version of blade's firmware */
        u16 lasttag;            /* last tag sent */
        u16 useme;
        } ip;
        ulong maxbcnt;
        struct list_head factive[NFACTIVE];     /* hash of active frames */
+       struct list_head rexmitq; /* deferred retransmissions */
        struct aoetgt *targets[NTARGETS];
        struct aoetgt **tgt;    /* target in use when working */
        struct aoetgt *htgt;    /* target needing rexmit assistance */
 struct sk_buff *aoecmd_ata_rsp(struct sk_buff *);
 void aoecmd_cfg_rsp(struct sk_buff *);
 void aoecmd_sleepwork(struct work_struct *);
+void aoecmd_wreset(struct aoetgt *t);
 void aoecmd_cleanslate(struct aoedev *);
 void aoecmd_exit(void);
 int aoecmd_init(void);
 
        return skb;
 }
 
+static struct frame *
+getframe_deferred(struct aoedev *d, u32 tag)
+{
+       struct list_head *head, *pos, *nx;
+       struct frame *f;
+
+       head = &d->rexmitq;
+       list_for_each_safe(pos, nx, head) {
+               f = list_entry(pos, struct frame, head);
+               if (f->tag == tag) {
+                       list_del(pos);
+                       return f;
+               }
+       }
+       return NULL;
+}
+
 static struct frame *
 getframe(struct aoedev *d, u32 tag)
 {
        return 1;
 }
 
+static void
+rexmit_deferred(struct aoedev *d)
+{
+       struct aoetgt *t;
+       struct frame *f;
+       struct list_head *pos, *nx, *head;
+
+       head = &d->rexmitq;
+       list_for_each_safe(pos, nx, head) {
+               f = list_entry(pos, struct frame, head);
+               t = f->t;
+               if (t->nout >= t->maxout)
+                       continue;
+               list_del(pos);
+               t->nout++;
+               resend(d, f);
+       }
+}
+
 static void
 rexmit_timer(ulong vp)
 {
        struct aoedev *d;
-       struct aoetgt *t, **tt, **te;
+       struct aoetgt *t;
        struct aoeif *ifp;
        struct frame *f;
        struct list_head *head, *pos, *nx;
 
        d = (struct aoedev *) vp;
 
-       /* timeout is always ~150% of the moving average */
-       timeout = d->rttavg;
-       timeout += timeout >> 1;
+       /* timeout based on observed timings and variations */
+       timeout = 2 * d->rttavg >> RTTSCALE;
+       timeout += 8 * d->rttdev >> RTTDSCALE;
+       if (timeout == 0)
+               timeout = 1;
 
        spin_lock_irqsave(&d->lock, flags);
 
                        list_move_tail(pos, &flist);
                }
        }
-       /* window check */
-       tt = d->targets;
-       te = tt + d->ntargets;
-       for (; tt < te && (t = *tt); tt++) {
-               if (t->nout == t->maxout
-               && t->maxout < t->nframes
-               && (jiffies - t->lastwadj)/HZ > 10) {
-                       t->maxout++;
-                       t->lastwadj = jiffies;
-               }
-       }
-
-       if (!list_empty(&flist)) {      /* retransmissions necessary */
-               n = d->rttavg <<= 1;
-               if (n > MAXTIMER)
-                       d->rttavg = MAXTIMER;
-       }
 
        /* process expired frames */
        while (!list_empty(&flist)) {
                pos = flist.next;
                f = list_entry(pos, struct frame, head);
-               n = f->waited += timeout;
+               n = f->waited += tsince(f->tag);
                n /= HZ;
                if (n > aoe_deadsecs) {
                        /* Waited too long.  Device failure.
                         */
                        list_splice(&flist, &d->factive[0]);
                        aoedev_downdev(d);
-                       break;
+                       goto out;
                }
-               list_del(pos);
 
                t = f->t;
                if (n > aoe_deadsecs/2)
                        d->htgt = t; /* see if another target can help */
 
-               if (t->nout == t->maxout) {
-                       if (t->maxout > 1)
-                               t->maxout--;
-                       t->lastwadj = jiffies;
+               if (t->maxout != 1) {
+                       t->ssthresh = t->maxout / 2;
+                       t->maxout = 1;
                }
 
                ifp = getif(t, f->skb->dev);
                        ejectif(t, ifp);
                        ifp = NULL;
                }
-               resend(d, f);
+               list_move_tail(pos, &d->rexmitq);
+               t->nout--;
        }
+       rexmit_deferred(d);
 
+out:
        if ((d->flags & DEVFL_KICKME || d->htgt) && d->blkq) {
                d->flags &= ~DEVFL_KICKME;
                d->blkq->request_fn(d->blkq);
 {
        if (d->htgt && !sthtith(d))
                return;
+       rexmit_deferred(d);
        while (aoecmd_ata_rw(d))
                ;
 }
 }
 
 static void
-calc_rttavg(struct aoedev *d, int rtt)
+calc_rttavg(struct aoedev *d, struct aoetgt *t, int rtt)
 {
        register long n;
 
        n = rtt;
-       if (n < 0) {
-               n = -rtt;
-               if (n < MINTIMER)
-                       n = MINTIMER;
-               else if (n > MAXTIMER)
-                       n = MAXTIMER;
-               d->mintimer += (n - d->mintimer) >> 1;
-       } else if (n < d->mintimer)
-               n = d->mintimer;
-       else if (n > MAXTIMER)
-               n = MAXTIMER;
-
-       /* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
-       n -= d->rttavg;
-       d->rttavg += n >> 2;
+
+       /* cf. Congestion Avoidance and Control, Jacobson & Karels, 1988 */
+       n -= d->rttavg >> RTTSCALE;
+       d->rttavg += n;
+       if (n < 0)
+               n = -n;
+       n -= d->rttdev >> RTTDSCALE;
+       d->rttdev += n;
+
+       if (!t || t->maxout >= t->nframes)
+               return;
+       if (t->maxout < t->ssthresh)
+               t->maxout += 1;
+       else if (t->nout == t->maxout && t->next_cwnd-- == 0) {
+               t->maxout += 1;
+               t->next_cwnd = t->maxout;
+       }
 }
 
 static struct aoetgt *
        struct aoedev *d;
        struct aoe_hdr *h;
        struct frame *f;
-       struct aoetgt *t;
        u32 n;
        ulong flags;
        char ebuf[128];
 
        n = be32_to_cpu(get_unaligned(&h->tag));
        f = getframe(d, n);
-       if (f == NULL) {
-               calc_rttavg(d, -tsince(n));
-               spin_unlock_irqrestore(&d->lock, flags);
-               aoedev_put(d);
-               snprintf(ebuf, sizeof ebuf,
-                       "%15s e%d.%d    tag=%08x@%08lx\n",
-                       "unexpected rsp",
-                       get_unaligned_be16(&h->major),
-                       h->minor,
-                       get_unaligned_be32(&h->tag),
-                       jiffies);
-               aoechr_error(ebuf);
-               return skb;
+       if (f) {
+               calc_rttavg(d, f->t, tsince(n));
+               f->t->nout--;
+       } else {
+               f = getframe_deferred(d, n);
+               if (f) {
+                       calc_rttavg(d, NULL, tsince(n));
+               } else {
+                       calc_rttavg(d, NULL, tsince(n));
+                       spin_unlock_irqrestore(&d->lock, flags);
+                       aoedev_put(d);
+                       snprintf(ebuf, sizeof(ebuf),
+                                "%15s e%d.%d    tag=%08x@%08lx\n",
+                                "unexpected rsp",
+                                get_unaligned_be16(&h->major),
+                                h->minor,
+                                get_unaligned_be32(&h->tag),
+                                jiffies);
+                       aoechr_error(ebuf);
+                       return skb;
+               }
        }
-       t = f->t;
-       calc_rttavg(d, tsince(f->tag));
-       t->nout--;
        aoecmd_work(d);
 
        spin_unlock_irqrestore(&d->lock, flags);
 
        skb->dev = t->ifp->nd;
 
-       d->rttavg = MAXTIMER;
+       d->rttavg = RTTAVG_INIT;
+       d->rttdev = RTTDEV_INIT;
        d->timer.function = rexmit_timer;
 
        return skb_clone(skb, GFP_ATOMIC);
        t->d = d;
        memcpy(t->addr, addr, sizeof t->addr);
        t->ifp = t->ifs;
-       t->maxout = t->nframes;
+       aoecmd_wreset(t);
        INIT_LIST_HEAD(&t->ffree);
        return *tt = t;
 }
        if (t) {
                t->nframes = n;
                if (n < t->maxout)
-                       t->maxout = n;
+                       aoecmd_wreset(t);
        } else {
                t = addtgt(d, h->src, n);
                if (!t)
        }
 }
 
+void
+aoecmd_wreset(struct aoetgt *t)
+{
+       t->maxout = 1;
+       t->ssthresh = t->nframes / 2;
+       t->next_cwnd = t->nframes;
+}
+
 void
 aoecmd_cleanslate(struct aoedev *d)
 {
        struct aoetgt **t, **te;
 
-       d->mintimer = MINTIMER;
+       d->rttavg = RTTAVG_INIT;
+       d->rttdev = RTTDEV_INIT;
        d->maxbcnt = 0;
 
        t = d->targets;
        te = t + NTARGETS;
        for (; t < te && *t; t++)
-               (*t)->maxout = (*t)->nframes;
+               aoecmd_wreset(*t);
 }
 
 void