diff options
Diffstat (limited to 'drivers/block/aoe/aoedev.c')
-rw-r--r-- | drivers/block/aoe/aoedev.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index e26f6f4a28a2..839a964906ce 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c | |||
@@ -7,11 +7,13 @@ | |||
7 | #include <linux/hdreg.h> | 7 | #include <linux/hdreg.h> |
8 | #include <linux/blkdev.h> | 8 | #include <linux/blkdev.h> |
9 | #include <linux/netdevice.h> | 9 | #include <linux/netdevice.h> |
10 | #include <linux/delay.h> | ||
10 | #include "aoe.h" | 11 | #include "aoe.h" |
11 | 12 | ||
12 | static void dummy_timer(ulong); | 13 | static void dummy_timer(ulong); |
13 | static void aoedev_freedev(struct aoedev *); | 14 | static void aoedev_freedev(struct aoedev *); |
14 | static void freetgt(struct aoetgt *t); | 15 | static void freetgt(struct aoedev *d, struct aoetgt *t); |
16 | static void skbpoolfree(struct aoedev *d); | ||
15 | 17 | ||
16 | static struct aoedev *devlist; | 18 | static struct aoedev *devlist; |
17 | static spinlock_t devlist_lock; | 19 | static spinlock_t devlist_lock; |
@@ -125,9 +127,10 @@ aoedev_freedev(struct aoedev *d) | |||
125 | t = d->targets; | 127 | t = d->targets; |
126 | e = t + NTARGETS; | 128 | e = t + NTARGETS; |
127 | for (; t < e && *t; t++) | 129 | for (; t < e && *t; t++) |
128 | freetgt(*t); | 130 | freetgt(d, *t); |
129 | if (d->bufpool) | 131 | if (d->bufpool) |
130 | mempool_destroy(d->bufpool); | 132 | mempool_destroy(d->bufpool); |
133 | skbpoolfree(d); | ||
131 | kfree(d); | 134 | kfree(d); |
132 | } | 135 | } |
133 | 136 | ||
@@ -176,6 +179,43 @@ aoedev_flush(const char __user *str, size_t cnt) | |||
176 | return 0; | 179 | return 0; |
177 | } | 180 | } |
178 | 181 | ||
182 | /* I'm not really sure that this is a realistic problem, but if the | ||
183 | network driver goes gonzo let's just leak memory after complaining. */ | ||
184 | static void | ||
185 | skbfree(struct sk_buff *skb) | ||
186 | { | ||
187 | enum { Sms = 100, Tms = 3*1000}; | ||
188 | int i = Tms / Sms; | ||
189 | |||
190 | if (skb == NULL) | ||
191 | return; | ||
192 | while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0) | ||
193 | msleep(Sms); | ||
194 | if (i <= 0) { | ||
195 | printk(KERN_ERR | ||
196 | "aoe: %s holds ref: %s\n", | ||
197 | skb->dev ? skb->dev->name : "netif", | ||
198 | "cannot free skb -- memory leaked."); | ||
199 | return; | ||
200 | } | ||
201 | skb_shinfo(skb)->nr_frags = skb->data_len = 0; | ||
202 | skb_trim(skb, 0); | ||
203 | dev_kfree_skb(skb); | ||
204 | } | ||
205 | |||
206 | static void | ||
207 | skbpoolfree(struct aoedev *d) | ||
208 | { | ||
209 | struct sk_buff *skb; | ||
210 | |||
211 | while ((skb = d->skbpool_hd)) { | ||
212 | d->skbpool_hd = skb->next; | ||
213 | skb->next = NULL; | ||
214 | skbfree(skb); | ||
215 | } | ||
216 | d->skbpool_tl = NULL; | ||
217 | } | ||
218 | |||
179 | /* find it or malloc it */ | 219 | /* find it or malloc it */ |
180 | struct aoedev * | 220 | struct aoedev * |
181 | aoedev_by_sysminor_m(ulong sysminor) | 221 | aoedev_by_sysminor_m(ulong sysminor) |
@@ -215,16 +255,14 @@ aoedev_by_sysminor_m(ulong sysminor) | |||
215 | } | 255 | } |
216 | 256 | ||
217 | static void | 257 | static void |
218 | freetgt(struct aoetgt *t) | 258 | freetgt(struct aoedev *d, struct aoetgt *t) |
219 | { | 259 | { |
220 | struct frame *f, *e; | 260 | struct frame *f, *e; |
221 | 261 | ||
222 | f = t->frames; | 262 | f = t->frames; |
223 | e = f + t->nframes; | 263 | e = f + t->nframes; |
224 | for (; f < e; f++) { | 264 | for (; f < e; f++) |
225 | skb_shinfo(f->skb)->nr_frags = 0; | 265 | skbfree(f->skb); |
226 | dev_kfree_skb(f->skb); | ||
227 | } | ||
228 | kfree(t->frames); | 266 | kfree(t->frames); |
229 | kfree(t); | 267 | kfree(t); |
230 | } | 268 | } |