aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/block/aoe/aoe.h5
-rw-r--r--drivers/block/aoe/aoecmd.c117
-rw-r--r--drivers/block/aoe/aoedev.c52
3 files changed, 133 insertions, 41 deletions
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 2248ab22657..67ef4d755e6 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -89,6 +89,7 @@ enum {
MIN_BUFS = 16,
NTARGETS = 8,
NAOEIFS = 8,
+ NSKBPOOLMAX = 128,
TIMERTICK = HZ / 10,
MINTIMER = HZ >> 2,
@@ -138,6 +139,7 @@ struct aoetgt {
u16 useme;
ulong lastwadj; /* last window adjustment */
int wpkts, rpkts;
+ int dataref;
};
struct aoedev {
@@ -159,6 +161,9 @@ struct aoedev {
spinlock_t lock;
struct sk_buff *sendq_hd; /* packets needing to be sent, list head */
struct sk_buff *sendq_tl;
+ struct sk_buff *skbpool_hd;
+ struct sk_buff *skbpool_tl;
+ int nskbpool;
mempool_t *bufpool; /* for deadlock-free Buf allocation */
struct list_head bufq; /* queue of bios to work on */
struct buf *inprocess; /* the one we're currently working on */
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 1be5150bcd3..b49e06ef121 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -106,45 +106,104 @@ ifrotate(struct aoetgt *t)
}
}
+static void
+skb_pool_put(struct aoedev *d, struct sk_buff *skb)
+{
+ if (!d->skbpool_hd)
+ d->skbpool_hd = skb;
+ else
+ d->skbpool_tl->next = skb;
+ d->skbpool_tl = skb;
+}
+
+static struct sk_buff *
+skb_pool_get(struct aoedev *d)
+{
+ struct sk_buff *skb;
+
+ skb = d->skbpool_hd;
+ if (skb && atomic_read(&skb_shinfo(skb)->dataref) == 1) {
+ d->skbpool_hd = skb->next;
+ skb->next = NULL;
+ return skb;
+ }
+ if (d->nskbpool < NSKBPOOLMAX
+ && (skb = new_skb(ETH_ZLEN))) {
+ d->nskbpool++;
+ return skb;
+ }
+ return NULL;
+}
+
+/* freeframe is where we do our load balancing so it's a little hairy. */
static struct frame *
freeframe(struct aoedev *d)
{
- struct frame *f, *e;
+ struct frame *f, *e, *rf;
struct aoetgt **t;
- ulong n;
+ struct sk_buff *skb;
if (d->targets[0] == NULL) { /* shouldn't happen, but I'm paranoid */
printk(KERN_ERR "aoe: NULL TARGETS!\n");
return NULL;
}
- t = d->targets;
- do {
- if (t != d->htgt
- && (*t)->ifp->nd
- && (*t)->nout < (*t)->maxout) {
- n = (*t)->nframes;
+ t = d->tgt;
+ t++;
+ if (t >= &d->targets[NTARGETS] || !*t)
+ t = d->targets;
+ for (;;) {
+ if ((*t)->nout < (*t)->maxout
+ && t != d->htgt
+ && (*t)->ifp->nd) {
+ rf = NULL;
f = (*t)->frames;
- e = f + n;
+ e = f + (*t)->nframes;
for (; f < e; f++) {
if (f->tag != FREETAG)
continue;
- if (atomic_read(&skb_shinfo(f->skb)->dataref)
+ skb = f->skb;
+ if (!skb
+ && !(f->skb = skb = new_skb(ETH_ZLEN)))
+ continue;
+ if (atomic_read(&skb_shinfo(skb)->dataref)
!= 1) {
- n--;
+ if (!rf)
+ rf = f;
continue;
}
- skb_shinfo(f->skb)->nr_frags = 0;
- f->skb->data_len = 0;
- skb_trim(f->skb, 0);
+gotone: skb_shinfo(skb)->nr_frags = skb->data_len = 0;
+ skb_trim(skb, 0);
d->tgt = t;
ifrotate(*t);
return f;
}
- if (n == 0) /* slow polling network card */
+ /* Work can be done, but the network layer is
+ holding our precious packets. Try to grab
+ one from the pool. */
+ f = rf;
+ if (f == NULL) { /* more paranoia */
+ printk(KERN_ERR
+ "aoe: freeframe: %s.\n",
+ "unexpected null rf");
+ d->flags |= DEVFL_KICKME;
+ return NULL;
+ }
+ skb = skb_pool_get(d);
+ if (skb) {
+ skb_pool_put(d, f->skb);
+ f->skb = skb;
+ goto gotone;
+ }
+ (*t)->dataref++;
+ if ((*t)->nout == 0)
d->flags |= DEVFL_KICKME;
}
+ if (t == d->tgt) /* we've looped and found nada */
+ break;
t++;
- } while (t < &d->targets[NTARGETS] && *t);
+ if (t >= &d->targets[NTARGETS] || !*t)
+ t = d->targets;
+ }
return NULL;
}
@@ -894,33 +953,23 @@ addtgt(struct aoedev *d, char *addr, ulong nframes)
return NULL;
t = kcalloc(1, sizeof *t, GFP_ATOMIC);
+ if (!t)
+ return NULL;
f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
- if (!t || !f)
- goto bail;
+ if (!f) {
+ kfree(t);
+ return NULL;
+ }
+
t->nframes = nframes;
t->frames = f;
e = f + nframes;
- for (; f < e; f++) {
+ for (; f < e; f++)
f->tag = FREETAG;
- f->skb = new_skb(ETH_ZLEN);
- if (!f->skb)
- break;
- }
- if (f != e) {
- while (f > t->frames) {
- f--;
- dev_kfree_skb(f->skb);
- }
- goto bail;
- }
memcpy(t->addr, addr, sizeof t->addr);
t->ifp = t->ifs;
t->maxout = t->nframes;
return *tt = t;
-bail:
- kfree(t);
- kfree(f);
- return NULL;
}
void
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
index e26f6f4a28a..839a964906c 100644
--- a/drivers/block/aoe/aoedev.c
+++ b/drivers/block/aoe/aoedev.c
@@ -7,11 +7,13 @@
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
+#include <linux/delay.h>
#include "aoe.h"
static void dummy_timer(ulong);
static void aoedev_freedev(struct aoedev *);
-static void freetgt(struct aoetgt *t);
+static void freetgt(struct aoedev *d, struct aoetgt *t);
+static void skbpoolfree(struct aoedev *d);
static struct aoedev *devlist;
static spinlock_t devlist_lock;
@@ -125,9 +127,10 @@ aoedev_freedev(struct aoedev *d)
t = d->targets;
e = t + NTARGETS;
for (; t < e && *t; t++)
- freetgt(*t);
+ freetgt(d, *t);
if (d->bufpool)
mempool_destroy(d->bufpool);
+ skbpoolfree(d);
kfree(d);
}
@@ -176,6 +179,43 @@ aoedev_flush(const char __user *str, size_t cnt)
return 0;
}
+/* I'm not really sure that this is a realistic problem, but if the
+network driver goes gonzo let's just leak memory after complaining. */
+static void
+skbfree(struct sk_buff *skb)
+{
+ enum { Sms = 100, Tms = 3*1000};
+ int i = Tms / Sms;
+
+ if (skb == NULL)
+ return;
+ while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0)
+ msleep(Sms);
+ if (i <= 0) {
+ printk(KERN_ERR
+ "aoe: %s holds ref: %s\n",
+ skb->dev ? skb->dev->name : "netif",
+ "cannot free skb -- memory leaked.");
+ return;
+ }
+ skb_shinfo(skb)->nr_frags = skb->data_len = 0;
+ skb_trim(skb, 0);
+ dev_kfree_skb(skb);
+}
+
+static void
+skbpoolfree(struct aoedev *d)
+{
+ struct sk_buff *skb;
+
+ while ((skb = d->skbpool_hd)) {
+ d->skbpool_hd = skb->next;
+ skb->next = NULL;
+ skbfree(skb);
+ }
+ d->skbpool_tl = NULL;
+}
+
/* find it or malloc it */
struct aoedev *
aoedev_by_sysminor_m(ulong sysminor)
@@ -215,16 +255,14 @@ aoedev_by_sysminor_m(ulong sysminor)
}
static void
-freetgt(struct aoetgt *t)
+freetgt(struct aoedev *d, struct aoetgt *t)
{
struct frame *f, *e;
f = t->frames;
e = f + t->nframes;
- for (; f < e; f++) {
- skb_shinfo(f->skb)->nr_frags = 0;
- dev_kfree_skb(f->skb);
- }
+ for (; f < e; f++)
+ skbfree(f->skb);
kfree(t->frames);
kfree(t);
}