Commit e407a7f6 authored by Ed L. Cashin's avatar Ed L. Cashin Committed by Greg Kroah-Hartman

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: default avatar"Ed L. Cashin" <ecashin@coraid.com>
Acked-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 2fdc0ea7
...@@ -107,11 +107,7 @@ struct frame { ...@@ -107,11 +107,7 @@ struct frame {
ulong waited; ulong waited;
struct buf *buf; struct buf *buf;
char *bufaddr; char *bufaddr;
int writedatalen; struct sk_buff *skb;
int ndata;
/* largest possible */
unsigned char data[sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr)];
}; };
struct aoedev { struct aoedev {
...@@ -157,6 +153,7 @@ void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor); ...@@ -157,6 +153,7 @@ void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor);
void aoecmd_ata_rsp(struct sk_buff *); void aoecmd_ata_rsp(struct sk_buff *);
void aoecmd_cfg_rsp(struct sk_buff *); void aoecmd_cfg_rsp(struct sk_buff *);
void aoecmd_sleepwork(void *vp); void aoecmd_sleepwork(void *vp);
struct sk_buff *new_skb(ulong);
int aoedev_init(void); int aoedev_init(void);
void aoedev_exit(void); void aoedev_exit(void);
......
...@@ -17,15 +17,14 @@ ...@@ -17,15 +17,14 @@
#define MAXTIMER (HZ << 1) #define MAXTIMER (HZ << 1)
#define MAXWAIT (60 * 3) /* After MAXWAIT seconds, give up and fail dev */ #define MAXWAIT (60 * 3) /* After MAXWAIT seconds, give up and fail dev */
static struct sk_buff * struct sk_buff *
new_skb(struct net_device *if_dev, ulong len) new_skb(ulong len)
{ {
struct sk_buff *skb; struct sk_buff *skb;
skb = alloc_skb(len, GFP_ATOMIC); skb = alloc_skb(len, GFP_ATOMIC);
if (skb) { if (skb) {
skb->nh.raw = skb->mac.raw = skb->data; skb->nh.raw = skb->mac.raw = skb->data;
skb->dev = if_dev;
skb->protocol = __constant_htons(ETH_P_AOE); skb->protocol = __constant_htons(ETH_P_AOE);
skb->priority = 0; skb->priority = 0;
skb_put(skb, len); skb_put(skb, len);
...@@ -40,29 +39,6 @@ new_skb(struct net_device *if_dev, ulong len) ...@@ -40,29 +39,6 @@ new_skb(struct net_device *if_dev, ulong len)
return skb; return skb;
} }
static struct sk_buff *
skb_prepare(struct aoedev *d, struct frame *f)
{
struct sk_buff *skb;
char *p;
skb = new_skb(d->ifp, f->ndata + f->writedatalen);
if (!skb) {
printk(KERN_INFO "aoe: skb_prepare: failure to allocate skb\n");
return NULL;
}
p = skb->mac.raw;
memcpy(p, f->data, f->ndata);
if (f->writedatalen) {
p += sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr);
memcpy(p, f->bufaddr, f->writedatalen);
}
return skb;
}
static struct frame * static struct frame *
getframe(struct aoedev *d, int tag) getframe(struct aoedev *d, int tag)
{ {
...@@ -129,10 +105,11 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) ...@@ -129,10 +105,11 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
bcnt = MAXATADATA; bcnt = MAXATADATA;
/* initialize the headers & frame */ /* initialize the headers & frame */
h = (struct aoe_hdr *) f->data; skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
ah = (struct aoe_atahdr *) (h+1); ah = (struct aoe_atahdr *) (h+1);
f->ndata = sizeof *h + sizeof *ah; skb->len = sizeof *h + sizeof *ah;
memset(h, 0, f->ndata); memset(h, 0, skb->len);
f->tag = aoehdr_atainit(d, h); f->tag = aoehdr_atainit(d, h);
f->waited = 0; f->waited = 0;
f->buf = buf; f->buf = buf;
...@@ -155,11 +132,13 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) ...@@ -155,11 +132,13 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
} }
if (bio_data_dir(buf->bio) == WRITE) { if (bio_data_dir(buf->bio) == WRITE) {
skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
offset_in_page(f->bufaddr), bcnt);
ah->aflags |= AOEAFL_WRITE; ah->aflags |= AOEAFL_WRITE;
f->writedatalen = bcnt;
} else { } else {
skb_shinfo(skb)->nr_frags = 0;
skb->len = ETH_ZLEN;
writebit = 0; writebit = 0;
f->writedatalen = 0;
} }
ah->cmdstat = WIN_READ | writebit | extbit; ah->cmdstat = WIN_READ | writebit | extbit;
...@@ -179,15 +158,14 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) ...@@ -179,15 +158,14 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset; buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
} }
skb = skb_prepare(d, f); skb->dev = d->ifp;
if (skb) { skb_get(skb);
skb->next = NULL; skb->next = NULL;
if (d->sendq_hd) if (d->sendq_hd)
d->sendq_tl->next = skb; d->sendq_tl->next = skb;
else else
d->sendq_hd = skb; d->sendq_hd = skb;
d->sendq_tl = skb; d->sendq_tl = skb;
}
} }
/* some callers cannot sleep, and they can call this function, /* 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,11 +187,12 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
if (!is_aoe_netif(ifp)) if (!is_aoe_netif(ifp))
continue; continue;
skb = new_skb(ifp, sizeof *h + sizeof *ch); skb = new_skb(sizeof *h + sizeof *ch);
if (skb == NULL) { if (skb == NULL) {
printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n"); printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n");
continue; continue;
} }
skb->dev = ifp;
if (sl_tail == NULL) if (sl_tail == NULL)
sl_tail = skb; sl_tail = skb;
h = (struct aoe_hdr *) skb->mac.raw; h = (struct aoe_hdr *) skb->mac.raw;
...@@ -283,21 +262,21 @@ rexmit(struct aoedev *d, struct frame *f) ...@@ -283,21 +262,21 @@ rexmit(struct aoedev *d, struct frame *f)
d->aoemajor, d->aoeminor, f->tag, jiffies, n); d->aoemajor, d->aoeminor, f->tag, jiffies, n);
aoechr_error(buf); aoechr_error(buf);
h = (struct aoe_hdr *) f->data; skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
f->tag = n; f->tag = n;
h->tag = cpu_to_be32(n); h->tag = cpu_to_be32(n);
memcpy(h->dst, d->addr, sizeof h->dst); memcpy(h->dst, d->addr, sizeof h->dst);
memcpy(h->src, d->ifp->dev_addr, sizeof h->src); memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
skb = skb_prepare(d, f); skb->dev = d->ifp;
if (skb) { skb_get(skb);
skb->next = NULL; skb->next = NULL;
if (d->sendq_hd) if (d->sendq_hd)
d->sendq_tl->next = skb; d->sendq_tl->next = skb;
else else
d->sendq_hd = skb; d->sendq_hd = skb;
d->sendq_tl = skb; d->sendq_tl = skb;
}
} }
static int static int
...@@ -514,7 +493,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) ...@@ -514,7 +493,7 @@ aoecmd_ata_rsp(struct sk_buff *skb)
calc_rttavg(d, tsince(f->tag)); calc_rttavg(d, tsince(f->tag));
ahin = (struct aoe_atahdr *) (hin+1); ahin = (struct aoe_atahdr *) (hin+1);
ahout = (struct aoe_atahdr *) (f->data + sizeof(struct aoe_hdr)); ahout = (struct aoe_atahdr *) (f->skb->mac.raw + sizeof(struct aoe_hdr));
buf = f->buf; buf = f->buf;
if (ahout->cmdstat == WIN_IDENTIFY) if (ahout->cmdstat == WIN_IDENTIFY)
...@@ -620,20 +599,21 @@ aoecmd_ata_id(struct aoedev *d) ...@@ -620,20 +599,21 @@ aoecmd_ata_id(struct aoedev *d)
} }
/* initialize the headers & frame */ /* initialize the headers & frame */
h = (struct aoe_hdr *) f->data; skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
ah = (struct aoe_atahdr *) (h+1); ah = (struct aoe_atahdr *) (h+1);
f->ndata = sizeof *h + sizeof *ah; skb->len = sizeof *h + sizeof *ah;
memset(h, 0, f->ndata); memset(h, 0, skb->len);
f->tag = aoehdr_atainit(d, h); f->tag = aoehdr_atainit(d, h);
f->waited = 0; f->waited = 0;
f->writedatalen = 0;
/* set up ata header */ /* set up ata header */
ah->scnt = 1; ah->scnt = 1;
ah->cmdstat = WIN_IDENTIFY; ah->cmdstat = WIN_IDENTIFY;
ah->lba3 = 0xa0; ah->lba3 = 0xa0;
skb = skb_prepare(d, f); skb->dev = d->ifp;
skb_get(skb);
d->rttavg = MAXTIMER; d->rttavg = MAXTIMER;
d->timer.function = rexmit_timer; d->timer.function = rexmit_timer;
......
...@@ -63,22 +63,32 @@ aoedev_newdev(ulong nframes) ...@@ -63,22 +63,32 @@ aoedev_newdev(ulong nframes)
struct frame *f, *e; struct frame *f, *e;
d = kzalloc(sizeof *d, GFP_ATOMIC); d = kzalloc(sizeof *d, GFP_ATOMIC);
if (d == NULL)
return NULL;
f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
if (f == NULL) { switch (!d || !f) {
kfree(d); case 0:
d->nframes = nframes;
d->frames = f;
e = f + nframes;
for (; f<e; f++) {
f->tag = FREETAG;
f->skb = new_skb(ETH_ZLEN);
if (!f->skb)
break;
}
if (f == e)
break;
while (f > d->frames) {
f--;
dev_kfree_skb(f->skb);
}
default:
if (f)
kfree(f);
if (d)
kfree(d);
return NULL; return NULL;
} }
INIT_WORK(&d->work, aoecmd_sleepwork, d); INIT_WORK(&d->work, aoecmd_sleepwork, d);
d->nframes = nframes;
d->frames = f;
e = f + nframes;
for (; f<e; f++)
f->tag = FREETAG;
spin_lock_init(&d->lock); spin_lock_init(&d->lock);
init_timer(&d->timer); init_timer(&d->timer);
d->timer.data = (ulong) d; d->timer.data = (ulong) d;
...@@ -160,11 +170,19 @@ aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt) ...@@ -160,11 +170,19 @@ aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt)
static void static void
aoedev_freedev(struct aoedev *d) aoedev_freedev(struct aoedev *d)
{ {
struct frame *f, *e;
if (d->gd) { if (d->gd) {
aoedisk_rm_sysfs(d); aoedisk_rm_sysfs(d);
del_gendisk(d->gd); del_gendisk(d->gd);
put_disk(d->gd); put_disk(d->gd);
} }
f = d->frames;
e = f + d->nframes;
for (; f<e; f++) {
skb_shinfo(f->skb)->nr_frags = 0;
dev_kfree_skb(f->skb);
}
kfree(d->frames); kfree(d->frames);
if (d->bufpool) if (d->bufpool)
mempool_destroy(d->bufpool); mempool_destroy(d->bufpool);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment