diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/aoe/aoe.h | 5 | ||||
-rw-r--r-- | drivers/block/aoe/aoecmd.c | 117 | ||||
-rw-r--r-- | drivers/block/aoe/aoedev.c | 52 |
3 files changed, 133 insertions, 41 deletions
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 2248ab226576..67ef4d755e64 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h | |||
@@ -89,6 +89,7 @@ enum { | |||
89 | MIN_BUFS = 16, | 89 | MIN_BUFS = 16, |
90 | NTARGETS = 8, | 90 | NTARGETS = 8, |
91 | NAOEIFS = 8, | 91 | NAOEIFS = 8, |
92 | NSKBPOOLMAX = 128, | ||
92 | 93 | ||
93 | TIMERTICK = HZ / 10, | 94 | TIMERTICK = HZ / 10, |
94 | MINTIMER = HZ >> 2, | 95 | MINTIMER = HZ >> 2, |
@@ -138,6 +139,7 @@ struct aoetgt { | |||
138 | u16 useme; | 139 | u16 useme; |
139 | ulong lastwadj; /* last window adjustment */ | 140 | ulong lastwadj; /* last window adjustment */ |
140 | int wpkts, rpkts; | 141 | int wpkts, rpkts; |
142 | int dataref; | ||
141 | }; | 143 | }; |
142 | 144 | ||
143 | struct aoedev { | 145 | struct aoedev { |
@@ -159,6 +161,9 @@ struct aoedev { | |||
159 | spinlock_t lock; | 161 | spinlock_t lock; |
160 | struct sk_buff *sendq_hd; /* packets needing to be sent, list head */ | 162 | struct sk_buff *sendq_hd; /* packets needing to be sent, list head */ |
161 | struct sk_buff *sendq_tl; | 163 | struct sk_buff *sendq_tl; |
164 | struct sk_buff *skbpool_hd; | ||
165 | struct sk_buff *skbpool_tl; | ||
166 | int nskbpool; | ||
162 | mempool_t *bufpool; /* for deadlock-free Buf allocation */ | 167 | mempool_t *bufpool; /* for deadlock-free Buf allocation */ |
163 | struct list_head bufq; /* queue of bios to work on */ | 168 | struct list_head bufq; /* queue of bios to work on */ |
164 | struct buf *inprocess; /* the one we're currently working on */ | 169 | 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 1be5150bcd3b..b49e06ef121e 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c | |||
@@ -106,45 +106,104 @@ ifrotate(struct aoetgt *t) | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | static void | ||
110 | skb_pool_put(struct aoedev *d, struct sk_buff *skb) | ||
111 | { | ||
112 | if (!d->skbpool_hd) | ||
113 | d->skbpool_hd = skb; | ||
114 | else | ||
115 | d->skbpool_tl->next = skb; | ||
116 | d->skbpool_tl = skb; | ||
117 | } | ||
118 | |||
119 | static struct sk_buff * | ||
120 | skb_pool_get(struct aoedev *d) | ||
121 | { | ||
122 | struct sk_buff *skb; | ||
123 | |||
124 | skb = d->skbpool_hd; | ||
125 | if (skb && atomic_read(&skb_shinfo(skb)->dataref) == 1) { | ||
126 | d->skbpool_hd = skb->next; | ||
127 | skb->next = NULL; | ||
128 | return skb; | ||
129 | } | ||
130 | if (d->nskbpool < NSKBPOOLMAX | ||
131 | && (skb = new_skb(ETH_ZLEN))) { | ||
132 | d->nskbpool++; | ||
133 | return skb; | ||
134 | } | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | /* freeframe is where we do our load balancing so it's a little hairy. */ | ||
109 | static struct frame * | 139 | static struct frame * |
110 | freeframe(struct aoedev *d) | 140 | freeframe(struct aoedev *d) |
111 | { | 141 | { |
112 | struct frame *f, *e; | 142 | struct frame *f, *e, *rf; |
113 | struct aoetgt **t; | 143 | struct aoetgt **t; |
114 | ulong n; | 144 | struct sk_buff *skb; |
115 | 145 | ||
116 | if (d->targets[0] == NULL) { /* shouldn't happen, but I'm paranoid */ | 146 | if (d->targets[0] == NULL) { /* shouldn't happen, but I'm paranoid */ |
117 | printk(KERN_ERR "aoe: NULL TARGETS!\n"); | 147 | printk(KERN_ERR "aoe: NULL TARGETS!\n"); |
118 | return NULL; | 148 | return NULL; |
119 | } | 149 | } |
120 | t = d->targets; | 150 | t = d->tgt; |
121 | do { | 151 | t++; |
122 | if (t != d->htgt | 152 | if (t >= &d->targets[NTARGETS] || !*t) |
123 | && (*t)->ifp->nd | 153 | t = d->targets; |
124 | && (*t)->nout < (*t)->maxout) { | 154 | for (;;) { |
125 | n = (*t)->nframes; | 155 | if ((*t)->nout < (*t)->maxout |
156 | && t != d->htgt | ||
157 | && (*t)->ifp->nd) { | ||
158 | rf = NULL; | ||
126 | f = (*t)->frames; | 159 | f = (*t)->frames; |
127 | e = f + n; | 160 | e = f + (*t)->nframes; |
128 | for (; f < e; f++) { | 161 | for (; f < e; f++) { |
129 | if (f->tag != FREETAG) | 162 | if (f->tag != FREETAG) |
130 | continue; | 163 | continue; |
131 | if (atomic_read(&skb_shinfo(f->skb)->dataref) | 164 | skb = f->skb; |
165 | if (!skb | ||
166 | && !(f->skb = skb = new_skb(ETH_ZLEN))) | ||
167 | continue; | ||
168 | if (atomic_read(&skb_shinfo(skb)->dataref) | ||
132 | != 1) { | 169 | != 1) { |
133 | n--; | 170 | if (!rf) |
171 | rf = f; | ||
134 | continue; | 172 | continue; |
135 | } | 173 | } |
136 | skb_shinfo(f->skb)->nr_frags = 0; | 174 | gotone: skb_shinfo(skb)->nr_frags = skb->data_len = 0; |
137 | f->skb->data_len = 0; | 175 | skb_trim(skb, 0); |
138 | skb_trim(f->skb, 0); | ||
139 | d->tgt = t; | 176 | d->tgt = t; |
140 | ifrotate(*t); | 177 | ifrotate(*t); |
141 | return f; | 178 | return f; |
142 | } | 179 | } |
143 | if (n == 0) /* slow polling network card */ | 180 | /* Work can be done, but the network layer is |
181 | holding our precious packets. Try to grab | ||
182 | one from the pool. */ | ||
183 | f = rf; | ||
184 | if (f == NULL) { /* more paranoia */ | ||
185 | printk(KERN_ERR | ||
186 | "aoe: freeframe: %s.\n", | ||
187 | "unexpected null rf"); | ||
188 | d->flags |= DEVFL_KICKME; | ||
189 | return NULL; | ||
190 | } | ||
191 | skb = skb_pool_get(d); | ||
192 | if (skb) { | ||
193 | skb_pool_put(d, f->skb); | ||
194 | f->skb = skb; | ||
195 | goto gotone; | ||
196 | } | ||
197 | (*t)->dataref++; | ||
198 | if ((*t)->nout == 0) | ||
144 | d->flags |= DEVFL_KICKME; | 199 | d->flags |= DEVFL_KICKME; |
145 | } | 200 | } |
201 | if (t == d->tgt) /* we've looped and found nada */ | ||
202 | break; | ||
146 | t++; | 203 | t++; |
147 | } while (t < &d->targets[NTARGETS] && *t); | 204 | if (t >= &d->targets[NTARGETS] || !*t) |
205 | t = d->targets; | ||
206 | } | ||
148 | return NULL; | 207 | return NULL; |
149 | } | 208 | } |
150 | 209 | ||
@@ -894,33 +953,23 @@ addtgt(struct aoedev *d, char *addr, ulong nframes) | |||
894 | return NULL; | 953 | return NULL; |
895 | 954 | ||
896 | t = kcalloc(1, sizeof *t, GFP_ATOMIC); | 955 | t = kcalloc(1, sizeof *t, GFP_ATOMIC); |
956 | if (!t) | ||
957 | return NULL; | ||
897 | f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); | 958 | f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); |
898 | if (!t || !f) | 959 | if (!f) { |
899 | goto bail; | 960 | kfree(t); |
961 | return NULL; | ||
962 | } | ||
963 | |||
900 | t->nframes = nframes; | 964 | t->nframes = nframes; |
901 | t->frames = f; | 965 | t->frames = f; |
902 | e = f + nframes; | 966 | e = f + nframes; |
903 | for (; f < e; f++) { | 967 | for (; f < e; f++) |
904 | f->tag = FREETAG; | 968 | f->tag = FREETAG; |
905 | f->skb = new_skb(ETH_ZLEN); | ||
906 | if (!f->skb) | ||
907 | break; | ||
908 | } | ||
909 | if (f != e) { | ||
910 | while (f > t->frames) { | ||
911 | f--; | ||
912 | dev_kfree_skb(f->skb); | ||
913 | } | ||
914 | goto bail; | ||
915 | } | ||
916 | memcpy(t->addr, addr, sizeof t->addr); | 969 | memcpy(t->addr, addr, sizeof t->addr); |
917 | t->ifp = t->ifs; | 970 | t->ifp = t->ifs; |
918 | t->maxout = t->nframes; | 971 | t->maxout = t->nframes; |
919 | return *tt = t; | 972 | return *tt = t; |
920 | bail: | ||
921 | kfree(t); | ||
922 | kfree(f); | ||
923 | return NULL; | ||
924 | } | 973 | } |
925 | 974 | ||
926 | void | 975 | void |
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 | } |