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