OSDN Git Service

aoe: become I/O request queue handler for increased user control
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / block / aoe / aoedev.c
1 /* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
2 /*
3  * aoedev.c
4  * AoE device utility functions; maintains device list.
5  */
6
7 #include <linux/hdreg.h>
8 #include <linux/blkdev.h>
9 #include <linux/netdevice.h>
10 #include <linux/delay.h>
11 #include <linux/slab.h>
12 #include "aoe.h"
13
14 static void dummy_timer(ulong);
15 static void aoedev_freedev(struct aoedev *);
16 static void freetgt(struct aoedev *d, struct aoetgt *t);
17 static void skbpoolfree(struct aoedev *d);
18
19 static struct aoedev *devlist;
20 static DEFINE_SPINLOCK(devlist_lock);
21
22 /*
23  * Users who grab a pointer to the device with aoedev_by_aoeaddr or
24  * aoedev_by_sysminor_m automatically get a reference count and must
25  * be responsible for performing a aoedev_put.  With the addition of
26  * async kthread processing I'm no longer confident that we can
27  * guarantee consistency in the face of device flushes.
28  *
29  * For the time being, we only bother to add extra references for
30  * frames sitting on the iocq.  When the kthreads finish processing
31  * these frames, they will aoedev_put the device.
32  */
33 struct aoedev *
34 aoedev_by_aoeaddr(int maj, int min)
35 {
36         struct aoedev *d;
37         ulong flags;
38
39         spin_lock_irqsave(&devlist_lock, flags);
40
41         for (d=devlist; d; d=d->next)
42                 if (d->aoemajor == maj && d->aoeminor == min) {
43                         d->ref++;
44                         break;
45                 }
46
47         spin_unlock_irqrestore(&devlist_lock, flags);
48         return d;
49 }
50
51 void
52 aoedev_put(struct aoedev *d)
53 {
54         ulong flags;
55
56         spin_lock_irqsave(&devlist_lock, flags);
57         d->ref--;
58         spin_unlock_irqrestore(&devlist_lock, flags);
59 }
60
61 static void
62 dummy_timer(ulong vp)
63 {
64         struct aoedev *d;
65
66         d = (struct aoedev *)vp;
67         if (d->flags & DEVFL_TKILL)
68                 return;
69         d->timer.expires = jiffies + HZ;
70         add_timer(&d->timer);
71 }
72
73 static void
74 aoe_failip(struct aoedev *d)
75 {
76         struct request *rq;
77         struct bio *bio;
78         unsigned long n;
79
80         aoe_failbuf(d, d->ip.buf);
81
82         rq = d->ip.rq;
83         if (rq == NULL)
84                 return;
85         while ((bio = d->ip.nxbio)) {
86                 clear_bit(BIO_UPTODATE, &bio->bi_flags);
87                 d->ip.nxbio = bio->bi_next;
88                 n = (unsigned long) rq->special;
89                 rq->special = (void *) --n;
90         }
91         if ((unsigned long) rq->special == 0)
92                 aoe_end_request(d, rq, 0);
93 }
94
95 void
96 aoedev_downdev(struct aoedev *d)
97 {
98         struct aoetgt *t, **tt, **te;
99         struct frame *f;
100         struct list_head *head, *pos, *nx;
101         struct request *rq;
102         int i;
103
104         d->flags &= ~DEVFL_UP;
105
106         /* clean out active buffers on all targets */
107         tt = d->targets;
108         te = tt + NTARGETS;
109         for (; tt < te && (t = *tt); tt++) {
110                 for (i = 0; i < NFACTIVE; i++) {
111                         head = &t->factive[i];
112                         list_for_each_safe(pos, nx, head) {
113                                 list_del(pos);
114                                 f = list_entry(pos, struct frame, head);
115                                 if (f->buf) {
116                                         f->buf->nframesout--;
117                                         aoe_failbuf(d, f->buf);
118                                 }
119                                 aoe_freetframe(f);
120                         }
121                 }
122                 t->maxout = t->nframes;
123                 t->nout = 0;
124         }
125
126         /* clean out the in-process request (if any) */
127         aoe_failip(d);
128         d->htgt = NULL;
129
130         /* fast fail all pending I/O */
131         if (d->blkq) {
132                 while ((rq = blk_peek_request(d->blkq))) {
133                         blk_start_request(rq);
134                         aoe_end_request(d, rq, 1);
135                 }
136         }
137
138         if (d->gd)
139                 set_capacity(d->gd, 0);
140 }
141
142 static void
143 aoedev_freedev(struct aoedev *d)
144 {
145         struct aoetgt **t, **e;
146
147         cancel_work_sync(&d->work);
148         if (d->gd) {
149                 aoedisk_rm_sysfs(d);
150                 del_gendisk(d->gd);
151                 put_disk(d->gd);
152                 blk_cleanup_queue(d->blkq);
153         }
154         t = d->targets;
155         e = t + NTARGETS;
156         for (; t < e && *t; t++)
157                 freetgt(d, *t);
158         if (d->bufpool)
159                 mempool_destroy(d->bufpool);
160         skbpoolfree(d);
161         kfree(d);
162 }
163
164 int
165 aoedev_flush(const char __user *str, size_t cnt)
166 {
167         ulong flags;
168         struct aoedev *d, **dd;
169         struct aoedev *rmd = NULL;
170         char buf[16];
171         int all = 0;
172
173         if (cnt >= 3) {
174                 if (cnt > sizeof buf)
175                         cnt = sizeof buf;
176                 if (copy_from_user(buf, str, cnt))
177                         return -EFAULT;
178                 all = !strncmp(buf, "all", 3);
179         }
180
181         spin_lock_irqsave(&devlist_lock, flags);
182         dd = &devlist;
183         while ((d = *dd)) {
184                 spin_lock(&d->lock);
185                 if ((!all && (d->flags & DEVFL_UP))
186                 || (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
187                 || d->nopen
188                 || d->ref) {
189                         spin_unlock(&d->lock);
190                         dd = &d->next;
191                         continue;
192                 }
193                 *dd = d->next;
194                 aoedev_downdev(d);
195                 d->flags |= DEVFL_TKILL;
196                 spin_unlock(&d->lock);
197                 d->next = rmd;
198                 rmd = d;
199         }
200         spin_unlock_irqrestore(&devlist_lock, flags);
201         while ((d = rmd)) {
202                 rmd = d->next;
203                 del_timer_sync(&d->timer);
204                 aoedev_freedev(d);      /* must be able to sleep */
205         }
206         return 0;
207 }
208
209 /* This has been confirmed to occur once with Tms=3*1000 due to the
210  * driver changing link and not processing its transmit ring.  The
211  * problem is hard enough to solve by returning an error that I'm
212  * still punting on "solving" this.
213  */
214 static void
215 skbfree(struct sk_buff *skb)
216 {
217         enum { Sms = 250, Tms = 30 * 1000};
218         int i = Tms / Sms;
219
220         if (skb == NULL)
221                 return;
222         while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0)
223                 msleep(Sms);
224         if (i < 0) {
225                 printk(KERN_ERR
226                         "aoe: %s holds ref: %s\n",
227                         skb->dev ? skb->dev->name : "netif",
228                         "cannot free skb -- memory leaked.");
229                 return;
230         }
231         skb->truesize -= skb->data_len;
232         skb_shinfo(skb)->nr_frags = skb->data_len = 0;
233         skb_trim(skb, 0);
234         dev_kfree_skb(skb);
235 }
236
237 static void
238 skbpoolfree(struct aoedev *d)
239 {
240         struct sk_buff *skb, *tmp;
241
242         skb_queue_walk_safe(&d->skbpool, skb, tmp)
243                 skbfree(skb);
244
245         __skb_queue_head_init(&d->skbpool);
246 }
247
248 /* find it or malloc it */
249 struct aoedev *
250 aoedev_by_sysminor_m(ulong sysminor)
251 {
252         struct aoedev *d;
253         ulong flags;
254
255         spin_lock_irqsave(&devlist_lock, flags);
256
257         for (d=devlist; d; d=d->next)
258                 if (d->sysminor == sysminor) {
259                         d->ref++;
260                         break;
261                 }
262         if (d)
263                 goto out;
264         d = kcalloc(1, sizeof *d, GFP_ATOMIC);
265         if (!d)
266                 goto out;
267         INIT_WORK(&d->work, aoecmd_sleepwork);
268         spin_lock_init(&d->lock);
269         skb_queue_head_init(&d->skbpool);
270         init_timer(&d->timer);
271         d->timer.data = (ulong) d;
272         d->timer.function = dummy_timer;
273         d->timer.expires = jiffies + HZ;
274         add_timer(&d->timer);
275         d->bufpool = NULL;      /* defer to aoeblk_gdalloc */
276         d->tgt = d->targets;
277         d->ref = 1;
278         d->sysminor = sysminor;
279         d->aoemajor = AOEMAJOR(sysminor);
280         d->aoeminor = AOEMINOR(sysminor);
281         d->mintimer = MINTIMER;
282         d->next = devlist;
283         devlist = d;
284  out:
285         spin_unlock_irqrestore(&devlist_lock, flags);
286         return d;
287 }
288
289 static void
290 freetgt(struct aoedev *d, struct aoetgt *t)
291 {
292         struct frame *f;
293         struct list_head *pos, *nx, *head;
294
295         head = &t->ffree;
296         list_for_each_safe(pos, nx, head) {
297                 list_del(pos);
298                 f = list_entry(pos, struct frame, head);
299                 skbfree(f->skb);
300                 kfree(f);
301         }
302         kfree(t);
303 }
304
305 void
306 aoedev_exit(void)
307 {
308         struct aoedev *d;
309         ulong flags;
310
311         aoe_flush_iocq();
312         while ((d = devlist)) {
313                 devlist = d->next;
314
315                 spin_lock_irqsave(&d->lock, flags);
316                 aoedev_downdev(d);
317                 d->flags |= DEVFL_TKILL;
318                 spin_unlock_irqrestore(&d->lock, flags);
319
320                 del_timer_sync(&d->timer);
321                 aoedev_freedev(d);
322         }
323 }
324
325 int __init
326 aoedev_init(void)
327 {
328         return 0;
329 }