aoe: user can ask driver to forget previously detected devices
[safe/jmp/linux-2.6] / drivers / block / aoe / aoedev.c
1 /* Copyright (c) 2006 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 "aoe.h"
11
12 static void dummy_timer(ulong);
13 static void aoedev_freedev(struct aoedev *);
14 static void freetgt(struct aoetgt *t);
15
16 static struct aoedev *devlist;
17 static spinlock_t devlist_lock;
18
19 int
20 aoedev_isbusy(struct aoedev *d)
21 {
22         struct aoetgt **t, **te;
23         struct frame *f, *e;
24
25         t = d->targets;
26         te = t + NTARGETS;
27         for (; t < te && *t; t++) {
28                 f = (*t)->frames;
29                 e = f + (*t)->nframes;
30                 for (; f < e; f++)
31                         if (f->tag != FREETAG)
32                                 return 1;
33         }
34         return 0;
35 }
36
37 struct aoedev *
38 aoedev_by_aoeaddr(int maj, int min)
39 {
40         struct aoedev *d;
41         ulong flags;
42
43         spin_lock_irqsave(&devlist_lock, flags);
44
45         for (d=devlist; d; d=d->next)
46                 if (d->aoemajor == maj && d->aoeminor == min)
47                         break;
48
49         spin_unlock_irqrestore(&devlist_lock, flags);
50         return d;
51 }
52
53 static void
54 dummy_timer(ulong vp)
55 {
56         struct aoedev *d;
57
58         d = (struct aoedev *)vp;
59         if (d->flags & DEVFL_TKILL)
60                 return;
61         d->timer.expires = jiffies + HZ;
62         add_timer(&d->timer);
63 }
64
65 void
66 aoedev_downdev(struct aoedev *d)
67 {
68         struct aoetgt **t, **te;
69         struct frame *f, *e;
70         struct buf *buf;
71         struct bio *bio;
72
73         t = d->targets;
74         te = t + NTARGETS;
75         for (; t < te && *t; t++) {
76                 f = (*t)->frames;
77                 e = f + (*t)->nframes;
78                 for (; f < e; f->tag = FREETAG, f->buf = NULL, f++) {
79                         if (f->tag == FREETAG || f->buf == NULL)
80                                 continue;
81                         buf = f->buf;
82                         bio = buf->bio;
83                         if (--buf->nframesout == 0
84                         && buf != d->inprocess) {
85                                 mempool_free(buf, d->bufpool);
86                                 bio_endio(bio, -EIO);
87                         }
88                 }
89                 (*t)->maxout = (*t)->nframes;
90                 (*t)->nout = 0;
91         }
92         buf = d->inprocess;
93         if (buf) {
94                 bio = buf->bio;
95                 mempool_free(buf, d->bufpool);
96                 bio_endio(bio, -EIO);
97         }
98         d->inprocess = NULL;
99         d->htgt = NULL;
100
101         while (!list_empty(&d->bufq)) {
102                 buf = container_of(d->bufq.next, struct buf, bufs);
103                 list_del(d->bufq.next);
104                 bio = buf->bio;
105                 mempool_free(buf, d->bufpool);
106                 bio_endio(bio, -EIO);
107         }
108
109         if (d->gd)
110                 d->gd->capacity = 0;
111
112         d->flags &= ~DEVFL_UP;
113 }
114
115 static void
116 aoedev_freedev(struct aoedev *d)
117 {
118         struct aoetgt **t, **e;
119
120         if (d->gd) {
121                 aoedisk_rm_sysfs(d);
122                 del_gendisk(d->gd);
123                 put_disk(d->gd);
124         }
125         t = d->targets;
126         e = t + NTARGETS;
127         for (; t < e && *t; t++)
128                 freetgt(*t);
129         if (d->bufpool)
130                 mempool_destroy(d->bufpool);
131         kfree(d);
132 }
133
134 int
135 aoedev_flush(const char __user *str, size_t cnt)
136 {
137         ulong flags;
138         struct aoedev *d, **dd;
139         struct aoedev *rmd = NULL;
140         char buf[16];
141         int all = 0;
142
143         if (cnt >= 3) {
144                 if (cnt > sizeof buf)
145                         cnt = sizeof buf;
146                 if (copy_from_user(buf, str, cnt))
147                         return -EFAULT;
148                 all = !strncmp(buf, "all", 3);
149         }
150
151         flush_scheduled_work();
152         spin_lock_irqsave(&devlist_lock, flags);
153         dd = &devlist;
154         while ((d = *dd)) {
155                 spin_lock(&d->lock);
156                 if ((!all && (d->flags & DEVFL_UP))
157                 || (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
158                 || d->nopen) {
159                         spin_unlock(&d->lock);
160                         dd = &d->next;
161                         continue;
162                 }
163                 *dd = d->next;
164                 aoedev_downdev(d);
165                 d->flags |= DEVFL_TKILL;
166                 spin_unlock(&d->lock);
167                 d->next = rmd;
168                 rmd = d;
169         }
170         spin_unlock_irqrestore(&devlist_lock, flags);
171         while ((d = rmd)) {
172                 rmd = d->next;
173                 del_timer_sync(&d->timer);
174                 aoedev_freedev(d);      /* must be able to sleep */
175         }
176         return 0;
177 }
178
179 /* find it or malloc it */
180 struct aoedev *
181 aoedev_by_sysminor_m(ulong sysminor)
182 {
183         struct aoedev *d;
184         ulong flags;
185
186         spin_lock_irqsave(&devlist_lock, flags);
187
188         for (d=devlist; d; d=d->next)
189                 if (d->sysminor == sysminor)
190                         break;
191         if (d)
192                 goto out;
193         d = kcalloc(1, sizeof *d, GFP_ATOMIC);
194         if (!d)
195                 goto out;
196         INIT_WORK(&d->work, aoecmd_sleepwork);
197         spin_lock_init(&d->lock);
198         init_timer(&d->timer);
199         d->timer.data = (ulong) d;
200         d->timer.function = dummy_timer;
201         d->timer.expires = jiffies + HZ;
202         add_timer(&d->timer);
203         d->bufpool = NULL;      /* defer to aoeblk_gdalloc */
204         d->tgt = d->targets;
205         INIT_LIST_HEAD(&d->bufq);
206         d->sysminor = sysminor;
207         d->aoemajor = AOEMAJOR(sysminor);
208         d->aoeminor = AOEMINOR(sysminor);
209         d->mintimer = MINTIMER;
210         d->next = devlist;
211         devlist = d;
212  out:
213         spin_unlock_irqrestore(&devlist_lock, flags);
214         return d;
215 }
216
217 static void
218 freetgt(struct aoetgt *t)
219 {
220         struct frame *f, *e;
221
222         f = t->frames;
223         e = f + t->nframes;
224         for (; f < e; f++) {
225                 skb_shinfo(f->skb)->nr_frags = 0;
226                 dev_kfree_skb(f->skb);
227         }
228         kfree(t->frames);
229         kfree(t);
230 }
231
232 void
233 aoedev_exit(void)
234 {
235         struct aoedev *d;
236         ulong flags;
237
238         flush_scheduled_work();
239
240         while ((d = devlist)) {
241                 devlist = d->next;
242
243                 spin_lock_irqsave(&d->lock, flags);
244                 aoedev_downdev(d);
245                 d->flags |= DEVFL_TKILL;
246                 spin_unlock_irqrestore(&d->lock, flags);
247
248                 del_timer_sync(&d->timer);
249                 aoedev_freedev(d);
250         }
251 }
252
253 int __init
254 aoedev_init(void)
255 {
256         spin_lock_init(&devlist_lock);
257         return 0;
258 }
259