blob: e26f6f4a28a2992203508b93ead845dfd26162e7 [file] [log] [blame]
Ed L. Cashin26114642006-09-20 14:36:48 -04001/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
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
Ed L. Cashin262bf542008-02-08 04:20:03 -080012static void dummy_timer(ulong);
13static void aoedev_freedev(struct aoedev *);
14static void freetgt(struct aoetgt *t);
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016static struct aoedev *devlist;
17static spinlock_t devlist_lock;
18
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050019int
20aoedev_isbusy(struct aoedev *d)
21{
Ed L. Cashin68e0d422008-02-08 04:20:00 -080022 struct aoetgt **t, **te;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050023 struct frame *f, *e;
24
Ed L. Cashin68e0d422008-02-08 04:20:00 -080025 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 }
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050034 return 0;
35}
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037struct aoedev *
ecashin@coraid.com32465c62005-04-18 22:00:18 -070038aoedev_by_aoeaddr(int maj, int min)
Linus Torvalds1da177e2005-04-16 15:20:36 -070039{
40 struct aoedev *d;
41 ulong flags;
42
43 spin_lock_irqsave(&devlist_lock, flags);
44
45 for (d=devlist; d; d=d->next)
ecashin@coraid.com32465c62005-04-18 22:00:18 -070046 if (d->aoemajor == maj && d->aoeminor == min)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 break;
48
49 spin_unlock_irqrestore(&devlist_lock, flags);
50 return d;
51}
52
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050053static void
54dummy_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
Linus Torvalds1da177e2005-04-16 15:20:36 -070065void
66aoedev_downdev(struct aoedev *d)
67{
Ed L. Cashin68e0d422008-02-08 04:20:00 -080068 struct aoetgt **t, **te;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 struct frame *f, *e;
70 struct buf *buf;
71 struct bio *bio;
72
Ed L. Cashin68e0d422008-02-08 04:20:00 -080073 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 }
Ed L. Cashin68e0d422008-02-08 04:20:00 -080089 (*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);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 }
98 d->inprocess = NULL;
Ed L. Cashin68e0d422008-02-08 04:20:00 -080099 d->htgt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
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);
NeilBrown6712ecf2007-09-27 12:47:43 +0200106 bio_endio(bio, -EIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 }
108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 if (d->gd)
110 d->gd->capacity = 0;
111
Ed L. Cashin68e0d422008-02-08 04:20:00 -0800112 d->flags &= ~DEVFL_UP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113}
114
Ed L. Cashin262bf542008-02-08 04:20:03 -0800115static void
116aoedev_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
134int
135aoedev_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
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500179/* find it or malloc it */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180struct aoedev *
Ed L. Cashin68e0d422008-02-08 04:20:00 -0800181aoedev_by_sysminor_m(ulong sysminor)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
183 struct aoedev *d;
184 ulong flags;
185
186 spin_lock_irqsave(&devlist_lock, flags);
187
188 for (d=devlist; d; d=d->next)
Ed L Cashin93d489f2005-04-29 10:24:22 -0400189 if (d->sysminor == sysminor)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 break;
Ed L. Cashin68e0d422008-02-08 04:20:00 -0800191 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:
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500213 spin_unlock_irqrestore(&devlist_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 return d;
215}
216
217static void
Ed L. Cashin68e0d422008-02-08 04:20:00 -0800218freetgt(struct aoetgt *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400220 struct frame *f, *e;
221
Ed L. Cashin68e0d422008-02-08 04:20:00 -0800222 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232void
233aoedev_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);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500245 d->flags |= DEVFL_TKILL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 spin_unlock_irqrestore(&d->lock, flags);
247
248 del_timer_sync(&d->timer);
249 aoedev_freedev(d);
250 }
251}
252
253int __init
254aoedev_init(void)
255{
256 spin_lock_init(&devlist_lock);
257 return 0;
258}
259