Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −5
Original line number Original line Diff line number Diff line
@@ -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);
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);
+37 −57
Original line number Original line Diff line number Diff line
@@ -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)
	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)
		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)
	}
	}


	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,8 +158,8 @@ 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;
@@ -188,7 +167,6 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
		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,
 * transmitting the packets later, when interrupts are on
 * transmitting the packets later, when interrupts are on
@@ -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,14 +262,15 @@ 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;
@@ -298,7 +278,6 @@ rexmit(struct aoedev *d, struct frame *f)
		d->sendq_hd = skb;
		d->sendq_hd = skb;
	d->sendq_tl = skb;
	d->sendq_tl = skb;
}
}
}


static int
static int
tsince(int tag)
tsince(int tag)
@@ -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)
	}
	}


	/* 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;
+30 −12
Original line number Original line Diff line number Diff line
@@ -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:
		return NULL;
	}

	INIT_WORK(&d->work, aoecmd_sleepwork, d);

 		d->nframes = nframes;
 		d->nframes = nframes;
 		d->frames = f;
 		d->frames = f;
 		e = f + nframes;
 		e = f + nframes;
	for (; f<e; f++)
 		for (; f<e; f++) {
 			f->tag = FREETAG;
 			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;
	}
	INIT_WORK(&d->work, aoecmd_sleepwork, d);
	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)
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);