aoechr.c revision 6bb6285fdb948cedee586c6bebc9ebc5e32a5c35
1/* Copyright (c) 2006 Coraid, Inc.  See COPYING for GPL terms. */
2/*
3 * aoechr.c
4 * AoE character device driver
5 */
6
7#include <linux/hdreg.h>
8#include <linux/blkdev.h>
9#include "aoe.h"
10
11enum {
12	//MINOR_STAT = 1, (moved to sysfs)
13	MINOR_ERR = 2,
14	MINOR_DISCOVER,
15	MINOR_INTERFACES,
16	MINOR_REVALIDATE,
17	MSGSZ = 2048,
18	NMSG = 100,		/* message backlog to retain */
19};
20
21struct aoe_chardev {
22	ulong minor;
23	char name[32];
24};
25
26enum { EMFL_VALID = 1 };
27
28struct ErrMsg {
29	short flags;
30	short len;
31	char *msg;
32};
33
34static struct ErrMsg emsgs[NMSG];
35static int emsgs_head_idx, emsgs_tail_idx;
36static struct semaphore emsgs_sema;
37static spinlock_t emsgs_lock;
38static int nblocked_emsgs_readers;
39static struct class *aoe_class;
40static struct aoe_chardev chardevs[] = {
41	{ MINOR_ERR, "err" },
42	{ MINOR_DISCOVER, "discover" },
43	{ MINOR_INTERFACES, "interfaces" },
44	{ MINOR_REVALIDATE, "revalidate" },
45};
46
47static int
48discover(void)
49{
50	aoecmd_cfg(0xffff, 0xff);
51	return 0;
52}
53
54static int
55interfaces(const char __user *str, size_t size)
56{
57	if (set_aoe_iflist(str, size)) {
58		eprintk("could not set interface list: too many interfaces\n");
59		return -EINVAL;
60	}
61	return 0;
62}
63
64static int
65revalidate(const char __user *str, size_t size)
66{
67	int major, minor, n;
68	ulong flags;
69	struct aoedev *d;
70	char buf[16];
71
72	if (size >= sizeof buf)
73		return -EINVAL;
74	buf[sizeof buf - 1] = '\0';
75	if (copy_from_user(buf, str, size))
76		return -EFAULT;
77
78	/* should be e%d.%d format */
79	n = sscanf(buf, "e%d.%d", &major, &minor);
80	if (n != 2) {
81		eprintk("invalid device specification\n");
82		return -EINVAL;
83	}
84	d = aoedev_by_aoeaddr(major, minor);
85	if (!d)
86		return -EINVAL;
87
88	spin_lock_irqsave(&d->lock, flags);
89	d->flags &= ~DEVFL_MAXBCNT;
90	d->flags |= DEVFL_PAUSE;
91	spin_unlock_irqrestore(&d->lock, flags);
92	aoecmd_cfg(major, minor);
93
94	return 0;
95}
96
97void
98aoechr_error(char *msg)
99{
100	struct ErrMsg *em;
101	char *mp;
102	ulong flags, n;
103
104	n = strlen(msg);
105
106	spin_lock_irqsave(&emsgs_lock, flags);
107
108	em = emsgs + emsgs_tail_idx;
109	if ((em->flags & EMFL_VALID)) {
110bail:		spin_unlock_irqrestore(&emsgs_lock, flags);
111		return;
112	}
113
114	mp = kmalloc(n, GFP_ATOMIC);
115	if (mp == NULL) {
116		eprintk("allocation failure, len=%ld\n", n);
117		goto bail;
118	}
119
120	memcpy(mp, msg, n);
121	em->msg = mp;
122	em->flags |= EMFL_VALID;
123	em->len = n;
124
125	emsgs_tail_idx++;
126	emsgs_tail_idx %= ARRAY_SIZE(emsgs);
127
128	spin_unlock_irqrestore(&emsgs_lock, flags);
129
130	if (nblocked_emsgs_readers)
131		up(&emsgs_sema);
132}
133
134static ssize_t
135aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
136{
137	int ret = -EINVAL;
138
139	switch ((unsigned long) filp->private_data) {
140	default:
141		iprintk("can't write to that file.\n");
142		break;
143	case MINOR_DISCOVER:
144		ret = discover();
145		break;
146	case MINOR_INTERFACES:
147		ret = interfaces(buf, cnt);
148		break;
149	case MINOR_REVALIDATE:
150		ret = revalidate(buf, cnt);
151	}
152	if (ret == 0)
153		ret = cnt;
154	return ret;
155}
156
157static int
158aoechr_open(struct inode *inode, struct file *filp)
159{
160	int n, i;
161
162	n = iminor(inode);
163	filp->private_data = (void *) (unsigned long) n;
164
165	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
166		if (chardevs[i].minor == n)
167			return 0;
168	return -EINVAL;
169}
170
171static int
172aoechr_rel(struct inode *inode, struct file *filp)
173{
174	return 0;
175}
176
177static ssize_t
178aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
179{
180	unsigned long n;
181	char *mp;
182	struct ErrMsg *em;
183	ssize_t len;
184	ulong flags;
185
186	n = (unsigned long) filp->private_data;
187	switch (n) {
188	case MINOR_ERR:
189		spin_lock_irqsave(&emsgs_lock, flags);
190loop:
191		em = emsgs + emsgs_head_idx;
192		if ((em->flags & EMFL_VALID) == 0) {
193			if (filp->f_flags & O_NDELAY) {
194				spin_unlock_irqrestore(&emsgs_lock, flags);
195				return -EAGAIN;
196			}
197			nblocked_emsgs_readers++;
198
199			spin_unlock_irqrestore(&emsgs_lock, flags);
200
201			n = down_interruptible(&emsgs_sema);
202
203			spin_lock_irqsave(&emsgs_lock, flags);
204
205			nblocked_emsgs_readers--;
206
207			if (n) {
208				spin_unlock_irqrestore(&emsgs_lock, flags);
209				return -ERESTARTSYS;
210			}
211			goto loop;
212		}
213		if (em->len > cnt) {
214			spin_unlock_irqrestore(&emsgs_lock, flags);
215			return -EAGAIN;
216		}
217		mp = em->msg;
218		len = em->len;
219		em->msg = NULL;
220		em->flags &= ~EMFL_VALID;
221
222		emsgs_head_idx++;
223		emsgs_head_idx %= ARRAY_SIZE(emsgs);
224
225		spin_unlock_irqrestore(&emsgs_lock, flags);
226
227		n = copy_to_user(buf, mp, len);
228		kfree(mp);
229		return n == 0 ? len : -EFAULT;
230	default:
231		return -EFAULT;
232	}
233}
234
235static struct file_operations aoe_fops = {
236	.write = aoechr_write,
237	.read = aoechr_read,
238	.open = aoechr_open,
239	.release = aoechr_rel,
240	.owner = THIS_MODULE,
241};
242
243int __init
244aoechr_init(void)
245{
246	int n, i;
247
248	n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
249	if (n < 0) {
250		eprintk("can't register char device\n");
251		return n;
252	}
253	sema_init(&emsgs_sema, 0);
254	spin_lock_init(&emsgs_lock);
255	aoe_class = class_create(THIS_MODULE, "aoe");
256	if (IS_ERR(aoe_class)) {
257		unregister_chrdev(AOE_MAJOR, "aoechr");
258		return PTR_ERR(aoe_class);
259	}
260	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
261		class_device_create(aoe_class, NULL,
262					MKDEV(AOE_MAJOR, chardevs[i].minor),
263					NULL, chardevs[i].name);
264
265	return 0;
266}
267
268void
269aoechr_exit(void)
270{
271	int i;
272
273	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
274		class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
275	class_destroy(aoe_class);
276	unregister_chrdev(AOE_MAJOR, "aoechr");
277}
278
279