xref: /openbmc/linux/drivers/block/aoe/aoechr.c (revision 68e0d42f)
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 <linux/delay.h>
10 #include "aoe.h"
11 
12 enum {
13 	//MINOR_STAT = 1, (moved to sysfs)
14 	MINOR_ERR = 2,
15 	MINOR_DISCOVER,
16 	MINOR_INTERFACES,
17 	MINOR_REVALIDATE,
18 	MSGSZ = 2048,
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_ERR
60 			"aoe: could not set interface list: too many interfaces\n");
61 		return -EINVAL;
62 	}
63 	return 0;
64 }
65 
66 static int
67 revalidate(const char __user *str, size_t size)
68 {
69 	int major, minor, n;
70 	ulong flags;
71 	struct aoedev *d;
72 	struct sk_buff *skb;
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: invalid device specification\n");
85 		return -EINVAL;
86 	}
87 	d = aoedev_by_aoeaddr(major, minor);
88 	if (!d)
89 		return -EINVAL;
90 	spin_lock_irqsave(&d->lock, flags);
91 	aoecmd_cleanslate(d);
92 loop:
93 	skb = aoecmd_ata_id(d);
94 	spin_unlock_irqrestore(&d->lock, flags);
95 	/* try again if we are able to sleep a bit,
96 	 * otherwise give up this revalidation
97 	 */
98 	if (!skb && !msleep_interruptible(200)) {
99 		spin_lock_irqsave(&d->lock, flags);
100 		goto loop;
101 	}
102 	aoenet_xmit(skb);
103 	aoecmd_cfg(major, minor);
104 	return 0;
105 }
106 
107 void
108 aoechr_error(char *msg)
109 {
110 	struct ErrMsg *em;
111 	char *mp;
112 	ulong flags, n;
113 
114 	n = strlen(msg);
115 
116 	spin_lock_irqsave(&emsgs_lock, flags);
117 
118 	em = emsgs + emsgs_tail_idx;
119 	if ((em->flags & EMFL_VALID)) {
120 bail:		spin_unlock_irqrestore(&emsgs_lock, flags);
121 		return;
122 	}
123 
124 	mp = kmalloc(n, GFP_ATOMIC);
125 	if (mp == NULL) {
126 		printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
127 		goto bail;
128 	}
129 
130 	memcpy(mp, msg, n);
131 	em->msg = mp;
132 	em->flags |= EMFL_VALID;
133 	em->len = n;
134 
135 	emsgs_tail_idx++;
136 	emsgs_tail_idx %= ARRAY_SIZE(emsgs);
137 
138 	spin_unlock_irqrestore(&emsgs_lock, flags);
139 
140 	if (nblocked_emsgs_readers)
141 		up(&emsgs_sema);
142 }
143 
144 static ssize_t
145 aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
146 {
147 	int ret = -EINVAL;
148 
149 	switch ((unsigned long) filp->private_data) {
150 	default:
151 		printk(KERN_INFO "aoe: can't write to that file.\n");
152 		break;
153 	case MINOR_DISCOVER:
154 		ret = discover();
155 		break;
156 	case MINOR_INTERFACES:
157 		ret = interfaces(buf, cnt);
158 		break;
159 	case MINOR_REVALIDATE:
160 		ret = revalidate(buf, cnt);
161 	}
162 	if (ret == 0)
163 		ret = cnt;
164 	return ret;
165 }
166 
167 static int
168 aoechr_open(struct inode *inode, struct file *filp)
169 {
170 	int n, i;
171 
172 	n = iminor(inode);
173 	filp->private_data = (void *) (unsigned long) n;
174 
175 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
176 		if (chardevs[i].minor == n)
177 			return 0;
178 	return -EINVAL;
179 }
180 
181 static int
182 aoechr_rel(struct inode *inode, struct file *filp)
183 {
184 	return 0;
185 }
186 
187 static ssize_t
188 aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
189 {
190 	unsigned long n;
191 	char *mp;
192 	struct ErrMsg *em;
193 	ssize_t len;
194 	ulong flags;
195 
196 	n = (unsigned long) filp->private_data;
197 	switch (n) {
198 	case MINOR_ERR:
199 		spin_lock_irqsave(&emsgs_lock, flags);
200 loop:
201 		em = emsgs + emsgs_head_idx;
202 		if ((em->flags & EMFL_VALID) == 0) {
203 			if (filp->f_flags & O_NDELAY) {
204 				spin_unlock_irqrestore(&emsgs_lock, flags);
205 				return -EAGAIN;
206 			}
207 			nblocked_emsgs_readers++;
208 
209 			spin_unlock_irqrestore(&emsgs_lock, flags);
210 
211 			n = down_interruptible(&emsgs_sema);
212 
213 			spin_lock_irqsave(&emsgs_lock, flags);
214 
215 			nblocked_emsgs_readers--;
216 
217 			if (n) {
218 				spin_unlock_irqrestore(&emsgs_lock, flags);
219 				return -ERESTARTSYS;
220 			}
221 			goto loop;
222 		}
223 		if (em->len > cnt) {
224 			spin_unlock_irqrestore(&emsgs_lock, flags);
225 			return -EAGAIN;
226 		}
227 		mp = em->msg;
228 		len = em->len;
229 		em->msg = NULL;
230 		em->flags &= ~EMFL_VALID;
231 
232 		emsgs_head_idx++;
233 		emsgs_head_idx %= ARRAY_SIZE(emsgs);
234 
235 		spin_unlock_irqrestore(&emsgs_lock, flags);
236 
237 		n = copy_to_user(buf, mp, len);
238 		kfree(mp);
239 		return n == 0 ? len : -EFAULT;
240 	default:
241 		return -EFAULT;
242 	}
243 }
244 
245 static const struct file_operations aoe_fops = {
246 	.write = aoechr_write,
247 	.read = aoechr_read,
248 	.open = aoechr_open,
249 	.release = aoechr_rel,
250 	.owner = THIS_MODULE,
251 };
252 
253 int __init
254 aoechr_init(void)
255 {
256 	int n, i;
257 
258 	n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
259 	if (n < 0) {
260 		printk(KERN_ERR "aoe: can't register char device\n");
261 		return n;
262 	}
263 	sema_init(&emsgs_sema, 0);
264 	spin_lock_init(&emsgs_lock);
265 	aoe_class = class_create(THIS_MODULE, "aoe");
266 	if (IS_ERR(aoe_class)) {
267 		unregister_chrdev(AOE_MAJOR, "aoechr");
268 		return PTR_ERR(aoe_class);
269 	}
270 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
271 		device_create(aoe_class, NULL,
272 			      MKDEV(AOE_MAJOR, chardevs[i].minor), chardevs[i].name);
273 
274 	return 0;
275 }
276 
277 void
278 aoechr_exit(void)
279 {
280 	int i;
281 
282 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
283 		device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
284 	class_destroy(aoe_class);
285 	unregister_chrdev(AOE_MAJOR, "aoechr");
286 }
287 
288