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 /drivers/block/aoe | |
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>
Diffstat (limited to 'drivers/block/aoe')
-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); |