diff options
| author | Ed Cashin <ecashin@coraid.com> | 2012-12-17 19:04:08 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-17 20:15:25 -0500 |
| commit | bbb44e30d07fdc111e34a5ec935b57521cea9499 (patch) | |
| tree | 11dda6628c4d7719607891f287c12aa87f06855b /drivers/block/aoe | |
| parent | b91316f2b7bc0b1d128a9890a2a4895f7e1c74fc (diff) | |
aoe: improve handling of misbehaving network paths
An AoE target can have multiple network ports used for AoE, and in the
aoe driver, those are tracked by the aoetgt struct. These changes allow
the aoe driver to handle network paths, or aoetgts, that are not working
well, compared to the others.
Paths that do not get responses despite the retransmission of AoE
commands are marked as "tainted", and non-tainted paths are preferred.
Meanwhile, the aoe driver attempts to "probe" the tainted path in the
background by issuing reads of LBA 0 that are padded out to full
(possibly jumbo-frame) size. If the probes get responses, then the path
is "redeemed", and its taint is removed.
This mechanism has been shown to be helpful in transparently handling
and recovering from real-world network "brown outs" in ways that the
earlier "shoot the help-needing target in the head" mechanism could not.
Signed-off-by: Ed Cashin <ecashin@coraid.com>
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/block/aoe')
| -rw-r--r-- | drivers/block/aoe/aoe.h | 11 | ||||
| -rw-r--r-- | drivers/block/aoe/aoecmd.c | 377 | ||||
| -rw-r--r-- | drivers/block/aoe/aoedev.c | 1 |
3 files changed, 268 insertions, 121 deletions
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index bfd765cf0eb7..b6d2b16358be 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h | |||
| @@ -91,6 +91,9 @@ enum { | |||
| 91 | RTTDSCALE = 3, | 91 | RTTDSCALE = 3, |
| 92 | RTTAVG_INIT = USEC_PER_SEC / 4 << RTTSCALE, | 92 | RTTAVG_INIT = USEC_PER_SEC / 4 << RTTSCALE, |
| 93 | RTTDEV_INIT = RTTAVG_INIT / 4, | 93 | RTTDEV_INIT = RTTAVG_INIT / 4, |
| 94 | |||
| 95 | HARD_SCORN_SECS = 10, /* try another remote port after this */ | ||
| 96 | MAX_TAINT = 1000, /* cap on aoetgt taint */ | ||
| 94 | }; | 97 | }; |
| 95 | 98 | ||
| 96 | struct buf { | 99 | struct buf { |
| @@ -103,6 +106,10 @@ struct buf { | |||
| 103 | struct request *rq; | 106 | struct request *rq; |
| 104 | }; | 107 | }; |
| 105 | 108 | ||
| 109 | enum frame_flags { | ||
| 110 | FFL_PROBE = 1, | ||
| 111 | }; | ||
| 112 | |||
| 106 | struct frame { | 113 | struct frame { |
| 107 | struct list_head head; | 114 | struct list_head head; |
| 108 | u32 tag; | 115 | u32 tag; |
| @@ -118,6 +125,7 @@ struct frame { | |||
| 118 | struct bio_vec *bv; | 125 | struct bio_vec *bv; |
| 119 | ulong bcnt; | 126 | ulong bcnt; |
| 120 | ulong bv_off; | 127 | ulong bv_off; |
| 128 | char flags; | ||
| 121 | }; | 129 | }; |
| 122 | 130 | ||
| 123 | struct aoeif { | 131 | struct aoeif { |
| @@ -138,8 +146,10 @@ struct aoetgt { | |||
| 138 | ushort next_cwnd; /* incr maxout after decrementing to zero */ | 146 | ushort next_cwnd; /* incr maxout after decrementing to zero */ |
| 139 | ushort ssthresh; /* slow start threshold */ | 147 | ushort ssthresh; /* slow start threshold */ |
| 140 | ulong falloc; /* number of allocated frames */ | 148 | ulong falloc; /* number of allocated frames */ |
| 149 | int taint; /* how much we want to avoid this aoetgt */ | ||
| 141 | int minbcnt; | 150 | int minbcnt; |
| 142 | int wpkts, rpkts; | 151 | int wpkts, rpkts; |
| 152 | char nout_probes; | ||
| 143 | }; | 153 | }; |
| 144 | 154 | ||
| 145 | struct aoedev { | 155 | struct aoedev { |
| @@ -174,7 +184,6 @@ struct aoedev { | |||
| 174 | struct list_head rexmitq; /* deferred retransmissions */ | 184 | struct list_head rexmitq; /* deferred retransmissions */ |
| 175 | struct aoetgt *targets[NTARGETS]; | 185 | struct aoetgt *targets[NTARGETS]; |
| 176 | struct aoetgt **tgt; /* target in use when working */ | 186 | struct aoetgt **tgt; /* target in use when working */ |
| 177 | struct aoetgt *htgt; /* target needing rexmit assistance */ | ||
| 178 | ulong ntargets; | 187 | ulong ntargets; |
| 179 | ulong kicked; | 188 | ulong kicked; |
| 180 | char ident[512]; | 189 | char ident[512]; |
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 391dd8ee2009..000f7fb48841 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #define MAXIOC (8192) /* default meant to avoid most soft lockups */ | 22 | #define MAXIOC (8192) /* default meant to avoid most soft lockups */ |
| 23 | 23 | ||
| 24 | static void ktcomplete(struct frame *, struct sk_buff *); | 24 | static void ktcomplete(struct frame *, struct sk_buff *); |
| 25 | static int count_targets(struct aoedev *d, int *untainted); | ||
| 25 | 26 | ||
| 26 | static struct buf *nextbuf(struct aoedev *); | 27 | static struct buf *nextbuf(struct aoedev *); |
| 27 | 28 | ||
| @@ -43,6 +44,8 @@ static struct { | |||
| 43 | spinlock_t lock; | 44 | spinlock_t lock; |
| 44 | } iocq; | 45 | } iocq; |
| 45 | 46 | ||
| 47 | static struct page *empty_page; | ||
| 48 | |||
| 46 | static struct sk_buff * | 49 | static struct sk_buff * |
| 47 | new_skb(ulong len) | 50 | new_skb(ulong len) |
| 48 | { | 51 | { |
| @@ -179,8 +182,10 @@ aoe_freetframe(struct frame *f) | |||
| 179 | 182 | ||
| 180 | t = f->t; | 183 | t = f->t; |
| 181 | f->buf = NULL; | 184 | f->buf = NULL; |
| 185 | f->lba = 0; | ||
| 182 | f->bv = NULL; | 186 | f->bv = NULL; |
| 183 | f->r_skb = NULL; | 187 | f->r_skb = NULL; |
| 188 | f->flags = 0; | ||
| 184 | list_add(&f->head, &t->ffree); | 189 | list_add(&f->head, &t->ffree); |
| 185 | } | 190 | } |
| 186 | 191 | ||
| @@ -234,20 +239,25 @@ newframe(struct aoedev *d) | |||
| 234 | struct frame *f; | 239 | struct frame *f; |
| 235 | struct aoetgt *t, **tt; | 240 | struct aoetgt *t, **tt; |
| 236 | int totout = 0; | 241 | int totout = 0; |
| 242 | int use_tainted; | ||
| 243 | int has_untainted; | ||
| 237 | 244 | ||
| 238 | if (d->targets[0] == NULL) { /* shouldn't happen, but I'm paranoid */ | 245 | if (d->targets[0] == NULL) { /* shouldn't happen, but I'm paranoid */ |
| 239 | printk(KERN_ERR "aoe: NULL TARGETS!\n"); | 246 | printk(KERN_ERR "aoe: NULL TARGETS!\n"); |
| 240 | return NULL; | 247 | return NULL; |
| 241 | } | 248 | } |
| 242 | tt = d->tgt; /* last used target */ | 249 | tt = d->tgt; /* last used target */ |
| 243 | for (;;) { | 250 | for (use_tainted = 0, has_untainted = 0;;) { |
| 244 | tt++; | 251 | tt++; |
| 245 | if (tt >= &d->targets[NTARGETS] || !*tt) | 252 | if (tt >= &d->targets[NTARGETS] || !*tt) |
| 246 | tt = d->targets; | 253 | tt = d->targets; |
| 247 | t = *tt; | 254 | t = *tt; |
| 248 | totout += t->nout; | 255 | if (!t->taint) { |
| 256 | has_untainted = 1; | ||
| 257 | totout += t->nout; | ||
| 258 | } | ||
| 249 | if (t->nout < t->maxout | 259 | if (t->nout < t->maxout |
| 250 | && t != d->htgt | 260 | && (use_tainted || !t->taint) |
| 251 | && t->ifp->nd) { | 261 | && t->ifp->nd) { |
| 252 | f = newtframe(d, t); | 262 | f = newtframe(d, t); |
| 253 | if (f) { | 263 | if (f) { |
| @@ -256,8 +266,12 @@ newframe(struct aoedev *d) | |||
| 256 | return f; | 266 | return f; |
| 257 | } | 267 | } |
| 258 | } | 268 | } |
| 259 | if (tt == d->tgt) /* we've looped and found nada */ | 269 | if (tt == d->tgt) { /* we've looped and found nada */ |
| 260 | break; | 270 | if (!use_tainted && !has_untainted) |
| 271 | use_tainted = 1; | ||
| 272 | else | ||
| 273 | break; | ||
| 274 | } | ||
| 261 | } | 275 | } |
| 262 | if (totout == 0) { | 276 | if (totout == 0) { |
| 263 | d->kicked++; | 277 | d->kicked++; |
| @@ -294,21 +308,68 @@ fhash(struct frame *f) | |||
| 294 | list_add_tail(&f->head, &d->factive[n]); | 308 | list_add_tail(&f->head, &d->factive[n]); |
| 295 | } | 309 | } |
| 296 | 310 | ||
| 311 | static void | ||
| 312 | ata_rw_frameinit(struct frame *f) | ||
| 313 | { | ||
| 314 | struct aoetgt *t; | ||
| 315 | struct aoe_hdr *h; | ||
| 316 | struct aoe_atahdr *ah; | ||
| 317 | struct sk_buff *skb; | ||
| 318 | char writebit, extbit; | ||
| 319 | |||
| 320 | skb = f->skb; | ||
| 321 | h = (struct aoe_hdr *) skb_mac_header(skb); | ||
| 322 | ah = (struct aoe_atahdr *) (h + 1); | ||
| 323 | skb_put(skb, sizeof(*h) + sizeof(*ah)); | ||
| 324 | memset(h, 0, skb->len); | ||
| 325 | |||
| 326 | writebit = 0x10; | ||
| 327 | extbit = 0x4; | ||
| 328 | |||
| 329 | t = f->t; | ||
| 330 | f->tag = aoehdr_atainit(t->d, t, h); | ||
| 331 | fhash(f); | ||
| 332 | t->nout++; | ||
| 333 | f->waited = 0; | ||
| 334 | f->waited_total = 0; | ||
| 335 | if (f->buf) | ||
| 336 | f->lba = f->buf->sector; | ||
| 337 | |||
| 338 | /* set up ata header */ | ||
| 339 | ah->scnt = f->bcnt >> 9; | ||
| 340 | put_lba(ah, f->lba); | ||
