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