blob: 6e231c5a119958dd3549e5d825f4706f9d04f9ec [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Copyright (c) 2004 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
12static struct aoedev *devlist;
13static spinlock_t devlist_lock;
14
15struct aoedev *
ecashin@coraid.com32465c62005-04-18 22:00:18 -070016aoedev_by_aoeaddr(int maj, int min)
Linus Torvalds1da177e2005-04-16 15:20:36 -070017{
18 struct aoedev *d;
19 ulong flags;
20
21 spin_lock_irqsave(&devlist_lock, flags);
22
23 for (d=devlist; d; d=d->next)
ecashin@coraid.com32465c62005-04-18 22:00:18 -070024 if (d->aoemajor == maj && d->aoeminor == min)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 break;
26
27 spin_unlock_irqrestore(&devlist_lock, flags);
28 return d;
29}
30
31/* called with devlist lock held */
32static struct aoedev *
33aoedev_newdev(ulong nframes)
34{
35 struct aoedev *d;
36 struct frame *f, *e;
37
38 d = kcalloc(1, sizeof *d, GFP_ATOMIC);
39 if (d == NULL)
40 return NULL;
41 f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
42 if (f == NULL) {
43 kfree(d);
44 return NULL;
45 }
46
47 d->nframes = nframes;
48 d->frames = f;
49 e = f + nframes;
50 for (; f<e; f++)
51 f->tag = FREETAG;
52
53 spin_lock_init(&d->lock);
54 init_timer(&d->timer);
55 d->bufpool = NULL; /* defer to aoeblk_gdalloc */
56 INIT_LIST_HEAD(&d->bufq);
57 d->next = devlist;
58 devlist = d;
59
60 return d;
61}
62
63void
64aoedev_downdev(struct aoedev *d)
65{
66 struct frame *f, *e;
67 struct buf *buf;
68 struct bio *bio;
69
70 d->flags |= DEVFL_TKILL;
71 del_timer(&d->timer);
72
73 f = d->frames;
74 e = f + d->nframes;
75 for (; f<e; f->tag = FREETAG, f->buf = NULL, f++) {
76 if (f->tag == FREETAG || f->buf == NULL)
77 continue;
78 buf = f->buf;
79 bio = buf->bio;
80 if (--buf->nframesout == 0) {
81 mempool_free(buf, d->bufpool);
82 bio_endio(bio, bio->bi_size, -EIO);
83 }
84 }
85 d->inprocess = NULL;
86
87 while (!list_empty(&d->bufq)) {
88 buf = container_of(d->bufq.next, struct buf, bufs);
89 list_del(d->bufq.next);
90 bio = buf->bio;
91 mempool_free(buf, d->bufpool);
92 bio_endio(bio, bio->bi_size, -EIO);
93 }
94
95 if (d->nopen)
96 d->flags |= DEVFL_CLOSEWAIT;
97 if (d->gd)
98 d->gd->capacity = 0;
99
100 d->flags &= ~DEVFL_UP;
101}
102
103struct aoedev *
104aoedev_set(ulong sysminor, unsigned char *addr, struct net_device *ifp, ulong bufcnt)
105{
106 struct aoedev *d;
107 ulong flags;
108
109 spin_lock_irqsave(&devlist_lock, flags);
110
111 for (d=devlist; d; d=d->next)
Ed L Cashin93d489f2005-04-29 10:24:22 -0400112 if (d->sysminor == sysminor)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 break;
114
115 if (d == NULL && (d = aoedev_newdev(bufcnt)) == NULL) {
116 spin_unlock_irqrestore(&devlist_lock, flags);
117 printk(KERN_INFO "aoe: aoedev_set: aoedev_newdev failure.\n");
118 return NULL;
Ed L Cashin93d489f2005-04-29 10:24:22 -0400119 } /* if newdev, (d->flags & DEVFL_UP) == 0 for below */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
121 spin_unlock_irqrestore(&devlist_lock, flags);
122 spin_lock_irqsave(&d->lock, flags);
123
124 d->ifp = ifp;
Ed L Cashin93d489f2005-04-29 10:24:22 -0400125 memcpy(d->addr, addr, sizeof d->addr);
126 if ((d->flags & DEVFL_UP) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 aoedev_downdev(d); /* flushes outstanding frames */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 d->sysminor = sysminor;
129 d->aoemajor = AOEMAJOR(sysminor);
130 d->aoeminor = AOEMINOR(sysminor);
131 }
132
133 spin_unlock_irqrestore(&d->lock, flags);
134 return d;
135}
136
137static void
138aoedev_freedev(struct aoedev *d)
139{
140 if (d->gd) {
141 aoedisk_rm_sysfs(d);
142 del_gendisk(d->gd);
143 put_disk(d->gd);
144 }
145 kfree(d->frames);
ecashin@coraid.com03347932005-04-18 22:00:19 -0700146 if (d->bufpool)
147 mempool_destroy(d->bufpool);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 kfree(d);
149}
150
151void
152aoedev_exit(void)
153{
154 struct aoedev *d;
155 ulong flags;
156
157 flush_scheduled_work();
158
159 while ((d = devlist)) {
160 devlist = d->next;
161
162 spin_lock_irqsave(&d->lock, flags);
163 aoedev_downdev(d);
164 spin_unlock_irqrestore(&d->lock, flags);
165
166 del_timer_sync(&d->timer);
167 aoedev_freedev(d);
168 }
169}
170
171int __init
172aoedev_init(void)
173{
174 spin_lock_init(&devlist_lock);
175 return 0;
176}
177