diff options
| author | Ed L. Cashin <ecashin@coraid.com> | 2006-09-20 14:36:49 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-10-18 15:53:50 -0400 |
| commit | e407a7f6cd143b3ab4eb3d7e1cf882e96b710eb5 (patch) | |
| tree | a180d2a6ae40b1fe6773c93a24a5c469948fd59d | |
| parent | 2fdc0ea75b26e3009cfdf72e79901e4e16bb99bd (diff) | |
aoe: zero copy write 1 of 2
Avoid memory copy on writes.
(This patch depends on fixes in patch 9 to follow.)
Although skb->len should not be set when working with linear skbuffs,
the skb->tail pointer maintained by skb_put/skb_trim is not relevant
to what happens when the skb_fill_page_desc function is called. This
issue was raised without comment in linux-kernel and netdev earlier
this month:
http://thread.gmane.org/gmane.linux.kernel/446474/
http://thread.gmane.org/gmane.linux.network/45444/
So until there is something analogous to skb_put that works for
zero-copy write skbuffs, we will do what the other callers of
skb_fill_page_desc are doing.
Signed-off-by: "Ed L. Cashin" <ecashin@coraid.com>
Acked-by: Alan Cox <alan@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/block/aoe/aoe.h | 7 | ||||
| -rw-r--r-- | drivers/block/aoe/aoecmd.c | 94 | ||||
| -rw-r--r-- | drivers/block/aoe/aoedev.c | 42 |
3 files changed, 69 insertions, 74 deletions
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 507c37799882..fa2d804b2665 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h | |||
| @@ -107,11 +107,7 @@ struct frame { | |||
| 107 | ulong waited; | 107 | ulong waited; |
| 108 | struct buf *buf; | 108 | struct buf *buf; |
| 109 | char *bufaddr; | 109 | char *bufaddr; |
| 110 | int writedatalen; | 110 | struct sk_buff *skb; |
| 111 | int ndata; | ||
| 112 | |||
| 113 | /* largest possible */ | ||
| 114 | unsigned char data[sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr)]; | ||
| 115 | }; | 111 | }; |
| 116 | 112 | ||
| 117 | struct aoedev { | 113 | struct aoedev { |
| @@ -157,6 +153,7 @@ void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor); | |||
| 157 | void aoecmd_ata_rsp(struct sk_buff *); | 153 | void aoecmd_ata_rsp(struct sk_buff *); |
| 158 | void aoecmd_cfg_rsp(struct sk_buff *); | 154 | void aoecmd_cfg_rsp(struct sk_buff *); |
| 159 | void aoecmd_sleepwork(void *vp); | 155 | void aoecmd_sleepwork(void *vp); |
| 156 | struct sk_buff *new_skb(ulong); | ||
| 160 | 157 | ||
| 161 | int aoedev_init(void); | 158 | int aoedev_init(void); |
| 162 | void aoedev_exit(void); | 159 | void aoedev_exit(void); |
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index d1d8759eca85..1aeb2969987f 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c | |||
| @@ -17,15 +17,14 @@ | |||
| 17 | #define MAXTIMER (HZ << 1) | 17 | #define MAXTIMER (HZ << 1) |
| 18 | #define MAXWAIT (60 * 3) /* After MAXWAIT seconds, give up and fail dev */ | 18 | #define MAXWAIT (60 * 3) /* After MAXWAIT seconds, give up and fail dev */ |
| 19 | 19 | ||
| 20 | static struct sk_buff * | 20 | struct sk_buff * |
| 21 | new_skb(struct net_device *if_dev, ulong len) | 21 | new_skb(ulong len) |
| 22 | { | 22 | { |
| 23 | struct sk_buff *skb; | 23 | struct sk_buff *skb; |
| 24 | 24 | ||
| 25 | skb = alloc_skb(len, GFP_ATOMIC); | 25 | skb = alloc_skb(len, GFP_ATOMIC); |
| 26 | if (skb) { | 26 | if (skb) { |
| 27 | skb->nh.raw = skb->mac.raw = skb->data; | 27 | skb->nh.raw = skb->mac.raw = skb->data; |
| 28 | skb->dev = if_dev; | ||
| 29 | skb->protocol = __constant_htons(ETH_P_AOE); | 28 | skb->protocol = __constant_htons(ETH_P_AOE); |
| 30 | skb->priority = 0; | 29 | skb->priority = 0; |
| 31 | skb_put(skb, len); | 30 | skb_put(skb, len); |
| @@ -40,29 +39,6 @@ new_skb(struct net_device *if_dev, ulong len) | |||
| 40 | return skb; | 39 | return skb; |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | static struct sk_buff * | ||
| 44 | skb_prepare(struct aoedev *d, struct frame *f) | ||
| 45 | { | ||
| 46 | struct sk_buff *skb; | ||
| 47 | char *p; | ||
| 48 | |||
| 49 | skb = new_skb(d->ifp, f->ndata + f->writedatalen); | ||
| 50 | if (!skb) { | ||
| 51 | printk(KERN_INFO "aoe: skb_prepare: failure to allocate skb\n"); | ||
| 52 | return NULL; | ||
| 53 | } | ||
| 54 | |||
| 55 | p = skb->mac.raw; | ||
| 56 | memcpy(p, f->data, f->ndata); | ||
| 57 | |||
| 58 | if (f->writedatalen) { | ||
| 59 | p += sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr); | ||
| 60 | memcpy(p, f->bufaddr, f->writedatalen); | ||
| 61 | } | ||
| 62 | |||
| 63 | return skb; | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct frame * | 42 | static struct frame * |
| 67 | getframe(struct aoedev *d, int tag) | 43 | getframe(struct aoedev *d, int tag) |
| 68 | { | 44 | { |
| @@ -129,10 +105,11 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) | |||
| 129 | bcnt = MAXATADATA; | 105 | bcnt = MAXATADATA; |
| 130 | 106 | ||
| 131 | /* initialize the headers & frame */ | 107 | /* initialize the headers & frame */ |
| 132 | h = (struct aoe_hdr *) f->data; | 108 | skb = f->skb; |
| 109 | h = (struct aoe_hdr *) skb->mac.raw; | ||
| 133 | ah = (struct aoe_atahdr *) (h+1); | 110 | ah = (struct aoe_atahdr *) (h+1); |
| 134 | f->ndata = sizeof *h + sizeof *ah; | 111 | skb->len = sizeof *h + sizeof *ah; |
| 135 | memset(h, 0, f->ndata); | 112 | memset(h, 0, skb->len); |
| 136 | f->tag = aoehdr_atainit(d, h); | 113 | f->tag = aoehdr_atainit(d, h); |
| 137 | f->waited = 0; | 114 | f->waited = 0; |
| 138 | f->buf = buf; | 115 | f->buf = buf; |
| @@ -155,11 +132,13 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) | |||
| 155 | } | 132 | } |
| 156 | 133 | ||
| 157 | if (bio_data_dir(buf->bio) == WRITE) { | 134 | if (bio_data_dir(buf->bio) == WRITE) { |
| 135 | skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr), | ||
| 136 | offset_in_page(f->bufaddr), bcnt); | ||
| 158 | ah->aflags |= AOEAFL_WRITE; | 137 | ah->aflags |= AOEAFL_WRITE; |
| 159 | f->writedatalen = bcnt; | ||
| 160 | } else { | 138 | } else { |
| 139 | skb_shinfo(skb)->nr_frags = 0; | ||
| 140 | skb->len = ETH_ZLEN; | ||
| 161 | writebit = 0; | 141 | writebit = 0; |
| 162 | f->writedatalen = 0; | ||
| 163 | } | 142 | } |
| 164 | 143 | ||
| 165 | ah->cmdstat = WIN_READ | writebit | extbit; | 144 | ah->cmdstat = WIN_READ | writebit | extbit; |
| @@ -179,15 +158,14 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) | |||
| 179 | buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset; | 158 | buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset; |
| 180 | } | 159 | } |
| 181 | 160 | ||
| 182 | skb = skb_prepare(d, f); | 161 | skb->dev = d->ifp; |
| 183 | if (skb) { | 162 | skb_get(skb); |
| 184 | skb->next = NULL; | 163 | skb->next = NULL; |
| 185 | if (d->sendq_hd) | 164 | if (d->sendq_hd) |
| 186 | d->sendq_tl->next = skb; | 165 | d->sendq_tl->next = skb; |
| 187 | else | 166 | else |
| 188 | d->sendq_hd = skb; | 167 | d->sendq_hd = skb; |
| 189 | d->sendq_tl = skb; | 168 | d->sendq_tl = skb; |
| 190 | } | ||
| 191 | } | 169 | } |
| 192 | 170 | ||
| 193 | /* some callers cannot sleep, and they can call this function, | 171 | /* some callers cannot sleep, and they can call this function, |
| @@ -209,11 +187,12 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail) | |||
| 209 | if (!is_aoe_netif(ifp)) | 187 | if (!is_aoe_netif(ifp)) |
| 210 | continue; | 188 | continue; |
| 211 | 189 | ||
| 212 | skb = new_skb(ifp, sizeof *h + sizeof *ch); | 190 | skb = new_skb(sizeof *h + sizeof *ch); |
| 213 | if (skb == NULL) { | 191 | if (skb == NULL) { |
| 214 | printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n"); | 192 | printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n"); |
| 215 | continue; | 193 | continue; |
| 216 | } | 194 | } |
| 195 | skb->dev = ifp; | ||
| 217 | if (sl_tail == NULL) | 196 | if (sl_tail == NULL) |
| 218 | sl_tail = skb; | 197 | sl_tail = skb; |
| 219 | h = (struct aoe_hdr *) skb->mac.raw; | 198 | h = (struct aoe_hdr *) skb->mac.raw; |
| @@ -283,21 +262,21 @@ rexmit(struct aoedev *d, struct frame *f) | |||
| 283 | d->aoemajor, d->aoeminor, f->tag, jiffies, n); | 262 | d->aoemajor, d->aoeminor, f->tag, jiffies, n); |
| 284 | aoechr_error(buf); | 263 | aoechr_error(buf); |
| 285 | 264 | ||
| 286 | h = (struct aoe_hdr *) f->data; | 265 | skb = f->skb; |
| 266 | h = (struct aoe_hdr *) skb->mac.raw; | ||
| 287 | f->tag = n; | 267 | f->tag = n; |
| 288 | h->tag = cpu_to_be32(n); | 268 | h->tag = cpu_to_be32(n); |
| 289 | memcpy(h->dst, d->addr, sizeof h->dst); | 269 | memcpy(h->dst, d->addr, sizeof h->dst); |
| 290 | memcpy(h->src, d->ifp->dev_addr, sizeof h->src); | 270 | memcpy(h->src, d->ifp->dev_addr, sizeof h->src); |
| 291 | 271 | ||
| 292 | skb = skb_prepare(d, f); | 272 | skb->dev = d->ifp; |
| 293 | if (skb) { | 273 | skb_get(skb); |
| 294 | skb->next = NULL; | 274 | skb->next = NULL; |
| 295 | if (d->sendq_hd) | 275 | if (d->sendq_hd) |
| 296 | d->sendq_tl->next = skb; | 276 | d->sendq_tl->next = skb; |
| 297 | else | 277 | else |
| 298 | d->sendq_hd = skb; | 278 | d->sendq_hd = skb; |
| 299 | d->sendq_tl = skb; | 279 | d->sendq_tl = skb; |
| 300 | } | ||
| 301 | } | 280 | } |
| 302 | 281 | ||
| 303 | static int | 282 | static int |
| @@ -514,7 +493,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) | |||
| 514 | calc_rttavg(d, tsince(f->tag)); | 493 | calc_rttavg(d, tsince(f->tag)); |
| 515 | 494 | ||
| 516 | ahin = (struct aoe_atahdr *) (hin+1); | 495 | ahin = (struct aoe_atahdr *) (hin+1); |
| 517 | ahout = (struct aoe_atahdr *) (f->data + sizeof(struct aoe_hdr)); | 496 | ahout = (struct aoe_atahdr *) (f->skb->mac.raw + sizeof(struct aoe_hdr)); |
| 518 | buf = f->buf; | 497 | buf = f->buf; |
| 519 | 498 | ||
| 520 | if (ahout->cmdstat == WIN_IDENTIFY) | 499 | if (ahout->cmdstat == WIN_IDENTIFY) |
| @@ -620,20 +599,21 @@ aoecmd_ata_id(struct aoedev *d) | |||
| 620 | } | 599 | } |
| 621 | 600 | ||
| 622 | /* initialize the headers & frame */ | 601 | /* initialize the headers & frame */ |
| 623 | h = (struct aoe_hdr *) f->data; | 602 | skb = f->skb; |
| 603 | h = (struct aoe_hdr *) skb->mac.raw; | ||
| 624 | ah = (struct aoe_atahdr *) (h+1); | 604 | ah = (struct aoe_atahdr *) (h+1); |
| 625 | f->ndata = sizeof *h + sizeof *ah; | 605 | skb->len = sizeof *h + sizeof *ah; |
| 626 | memset(h, 0, f->ndata); | 606 | memset(h, 0, skb->len); |
| 627 | f->tag = aoehdr_atainit(d, h); | 607 | f->tag = aoehdr_atainit(d, h); |
| 628 | f->waited = 0; | 608 | f->waited = 0; |
| 629 | f->writedatalen = 0; | ||
| 630 | 609 | ||
| 631 | /* set up ata header */ | 610 | /* set up ata header */ |
| 632 | ah->scnt = 1; | 611 | ah->scnt = 1; |
| 633 | ah->cmdstat = WIN_IDENTIFY; | 612 | ah->cmdstat = WIN_IDENTIFY; |
| 634 | ah->lba3 = 0xa0; | 613 | ah->lba3 = 0xa0; |
| 635 | 614 | ||
| 636 | skb = skb_prepare(d, f); | 615 | skb->dev = d->ifp; |
| 616 | skb_get(skb); | ||
| 637 | 617 | ||
| 638 | d->rttavg = MAXTIMER; | 618 | d->rttavg = MAXTIMER; |
| 639 | d->timer.function = rexmit_timer; | 619 | d->timer.function = rexmit_timer; |
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index c7e05ed82512..abf1d3c073e3 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c | |||
| @@ -63,22 +63,32 @@ aoedev_newdev(ulong nframes) | |||
| 63 | struct frame *f, *e; | 63 | struct frame *f, *e; |
| 64 | 64 | ||
| 65 | d = kzalloc(sizeof *d, GFP_ATOMIC); | 65 | d = kzalloc(sizeof *d, GFP_ATOMIC); |
| 66 | if (d == NULL) | ||
| 67 | return NULL; | ||
| 68 | f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); | 66 | f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); |
| 69 | if (f == NULL) { | 67 | switch (!d || !f) { |
| 70 | kfree(d); | 68 | case 0: |
| 69 | d->nframes = nframes; | ||
| 70 | d->frames = f; | ||
| 71 | e = f + nframes; | ||
| 72 | for (; f<e; f++) { | ||
| 73 | f->tag = FREETAG; | ||
| 74 | f->skb = new_skb(ETH_ZLEN); | ||
| 75 | if (!f->skb) | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | if (f == e) | ||
| 79 | break; | ||
| 80 | while (f > d->frames) { | ||
| 81 | f--; | ||
| 82 | dev_kfree_skb(f->skb); | ||
| 83 | } | ||
| 84 | default: | ||
| 85 | if (f) | ||
| 86 | kfree(f); | ||
| 87 | if (d) | ||
| 88 | kfree(d); | ||
| 71 | return NULL; | 89 | return NULL; |
| 72 | } | 90 | } |
| 73 | |||
| 74 | INIT_WORK(&d->work, aoecmd_sleepwork, d); | 91 | INIT_WORK(&d->work, aoecmd_sleepwork, d); |
| 75 | |||
| 76 | d->nframes = nframes; | ||
| 77 | d->frames = f; | ||
| 78 | e = f + nframes; | ||
| 79 | for (; f<e; f++) | ||
| 80 | f->tag = FREETAG; | ||
| 81 | |||
| 82 | spin_lock_init(&d->lock); | 92 | spin_lock_init(&d->lock); |
| 83 | init_timer(&d->timer); | 93 | init_timer(&d->timer); |
| 84 | d->timer.data = (ulong) d; | 94 | d->timer.data = (ulong) d; |
| @@ -160,11 +170,19 @@ aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt) | |||
| 160 | static void | 170 | static void |
| 161 | aoedev_freedev(struct aoedev *d) | 171 | aoedev_freedev(struct aoedev *d) |
| 162 | { | 172 | { |
| 173 | struct frame *f, *e; | ||
| 174 | |||
| 163 | if (d->gd) { | 175 | if (d->gd) { |
| 164 | aoedisk_rm_sysfs(d); | 176 | aoedisk_rm_sysfs(d); |
| 165 | del_gendisk(d->gd); | 177 | del_gendisk(d->gd); |
| 166 | put_disk(d->gd); | 178 | put_disk(d->gd); |
| 167 | } | 179 | } |
| 180 | f = d->frames; | ||
| 181 | e = f + d->nframes; | ||
| 182 | for (; f<e; f++) { | ||
| 183 | skb_shinfo(f->skb)->nr_frags = 0; | ||
| 184 | dev_kfree_skb(f->skb); | ||
| 185 | } | ||
| 168 | kfree(d->frames); | 186 | kfree(d->frames); |
| 169 | if (d->bufpool) | 187 | if (d->bufpool) |
| 170 | mempool_destroy(d->bufpool); | 188 | mempool_destroy(d->bufpool); |
