xref: /openbmc/linux/drivers/block/aoe/aoedev.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1 /* 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 
12 static struct aoedev *devlist;
13 static spinlock_t devlist_lock;
14 
15 struct aoedev *
16 aoedev_bymac(unsigned char *macaddr)
17 {
18 	struct aoedev *d;
19 	ulong flags;
20 
21 	spin_lock_irqsave(&devlist_lock, flags);
22 
23 	for (d=devlist; d; d=d->next)
24 		if (!memcmp(d->addr, macaddr, 6))
25 			break;
26 
27 	spin_unlock_irqrestore(&devlist_lock, flags);
28 	return d;
29 }
30 
31 /* called with devlist lock held */
32 static struct aoedev *
33 aoedev_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 
63 void
64 aoedev_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 
103 struct aoedev *
104 aoedev_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)
112 		if (d->sysminor == sysminor
113 		|| memcmp(d->addr, addr, sizeof d->addr) == 0)
114 			break;
115 
116 	if (d == NULL && (d = aoedev_newdev(bufcnt)) == NULL) {
117 		spin_unlock_irqrestore(&devlist_lock, flags);
118 		printk(KERN_INFO "aoe: aoedev_set: aoedev_newdev failure.\n");
119 		return NULL;
120 	}
121 
122 	spin_unlock_irqrestore(&devlist_lock, flags);
123 	spin_lock_irqsave(&d->lock, flags);
124 
125 	d->ifp = ifp;
126 
127 	if (d->sysminor != sysminor
128 	|| memcmp(d->addr, addr, sizeof d->addr)
129 	|| (d->flags & DEVFL_UP) == 0) {
130 		aoedev_downdev(d); /* flushes outstanding frames */
131 		memcpy(d->addr, addr, sizeof d->addr);
132 		d->sysminor = sysminor;
133 		d->aoemajor = AOEMAJOR(sysminor);
134 		d->aoeminor = AOEMINOR(sysminor);
135 	}
136 
137 	spin_unlock_irqrestore(&d->lock, flags);
138 	return d;
139 }
140 
141 static void
142 aoedev_freedev(struct aoedev *d)
143 {
144 	if (d->gd) {
145 		aoedisk_rm_sysfs(d);
146 		del_gendisk(d->gd);
147 		put_disk(d->gd);
148 	}
149 	kfree(d->frames);
150 	mempool_destroy(d->bufpool);
151 	kfree(d);
152 }
153 
154 void
155 aoedev_exit(void)
156 {
157 	struct aoedev *d;
158 	ulong flags;
159 
160 	flush_scheduled_work();
161 
162 	while ((d = devlist)) {
163 		devlist = d->next;
164 
165 		spin_lock_irqsave(&d->lock, flags);
166 		aoedev_downdev(d);
167 		spin_unlock_irqrestore(&d->lock, flags);
168 
169 		del_timer_sync(&d->timer);
170 		aoedev_freedev(d);
171 	}
172 }
173 
174 int __init
175 aoedev_init(void)
176 {
177 	spin_lock_init(&devlist_lock);
178 	return 0;
179 }
180 
181