blob: f1124664c5c907174bad6a0387ff53bb39130251 [file] [log] [blame]
Ed L. Cashin26114642006-09-20 14:36:48 -04001/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * aoechr.c
4 * AoE character device driver
5 */
6
7#include <linux/hdreg.h>
8#include <linux/blkdev.h>
Ed L. Cashin68e0d422008-02-08 04:20:00 -08009#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include "aoe.h"
11
12enum {
13 //MINOR_STAT = 1, (moved to sysfs)
14 MINOR_ERR = 2,
15 MINOR_DISCOVER,
16 MINOR_INTERFACES,
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050017 MINOR_REVALIDATE,
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 MSGSZ = 2048,
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 NMSG = 100, /* message backlog to retain */
20};
21
22struct aoe_chardev {
23 ulong minor;
24 char name[32];
25};
26
27enum { EMFL_VALID = 1 };
28
29struct ErrMsg {
30 short flags;
31 short len;
32 char *msg;
33};
34
35static struct ErrMsg emsgs[NMSG];
36static int emsgs_head_idx, emsgs_tail_idx;
37static struct semaphore emsgs_sema;
38static spinlock_t emsgs_lock;
39static int nblocked_emsgs_readers;
gregkh@suse.dedeb36972005-03-23 09:52:10 -080040static struct class *aoe_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041static struct aoe_chardev chardevs[] = {
42 { MINOR_ERR, "err" },
43 { MINOR_DISCOVER, "discover" },
44 { MINOR_INTERFACES, "interfaces" },
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050045 { MINOR_REVALIDATE, "revalidate" },
Linus Torvalds1da177e2005-04-16 15:20:36 -070046};
47
48static int
49discover(void)
50{
51 aoecmd_cfg(0xffff, 0xff);
52 return 0;
53}
54
55static int
56interfaces(const char __user *str, size_t size)
57{
58 if (set_aoe_iflist(str, size)) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -040059 printk(KERN_ERR
60 "aoe: could not set interface list: too many interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 return -EINVAL;
62 }
63 return 0;
64}
65
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050066static int
67revalidate(const char __user *str, size_t size)
68{
69 int major, minor, n;
70 ulong flags;
71 struct aoedev *d;
Ed L. Cashin68e0d422008-02-08 04:20:00 -080072 struct sk_buff *skb;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050073 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) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -040084 printk(KERN_ERR "aoe: invalid device specification\n");
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050085 return -EINVAL;
86 }
87 d = aoedev_by_aoeaddr(major, minor);
88 if (!d)
89 return -EINVAL;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050090 spin_lock_irqsave(&d->lock, flags);
Ed L. Cashin68e0d422008-02-08 04:20:00 -080091 aoecmd_cleanslate(d);
92loop:
93 skb = aoecmd_ata_id(d);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050094 spin_unlock_irqrestore(&d->lock, flags);
Ed L. Cashin68e0d422008-02-08 04:20:00 -080095 /* 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);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500103 aoecmd_cfg(major, minor);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500104 return 0;
105}
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107void
108aoechr_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)) {
120bail: spin_unlock_irqrestore(&emsgs_lock, flags);
121 return;
122 }
123
124 mp = kmalloc(n, GFP_ATOMIC);
125 if (mp == NULL) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400126 printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 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
144static ssize_t
145aoechr_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:
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400151 printk(KERN_INFO "aoe: can't write to that file.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 break;
153 case MINOR_DISCOVER:
154 ret = discover();
155 break;
156 case MINOR_INTERFACES:
157 ret = interfaces(buf, cnt);
158 break;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500159 case MINOR_REVALIDATE:
160 ret = revalidate(buf, cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
162 if (ret == 0)
163 ret = cnt;
164 return ret;
165}
166
167static int
168aoechr_open(struct inode *inode, struct file *filp)
169{
170 int n, i;
171
Eric Sesterhenn2017b372006-07-10 04:45:32 -0700172 n = iminor(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 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
181static int
182aoechr_rel(struct inode *inode, struct file *filp)
183{
184 return 0;
185}
186
187static ssize_t
188aoechr_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;
Ed L. Cashincf446f02008-02-08 04:20:03 -0800197 if (n != MINOR_ERR)
198 return -EFAULT;
199
200 spin_lock_irqsave(&emsgs_lock, flags);
201
202 for (;;) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 em = emsgs + emsgs_head_idx;
Ed L. Cashincf446f02008-02-08 04:20:03 -0800204 if ((em->flags & EMFL_VALID) != 0)
205 break;
206 if (filp->f_flags & O_NDELAY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 spin_unlock_irqrestore(&emsgs_lock, flags);
208 return -EAGAIN;
209 }
Ed L. Cashincf446f02008-02-08 04:20:03 -0800210 nblocked_emsgs_readers++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
212 spin_unlock_irqrestore(&emsgs_lock, flags);
213
Ed L. Cashincf446f02008-02-08 04:20:03 -0800214 n = down_interruptible(&emsgs_sema);
215
216 spin_lock_irqsave(&emsgs_lock, flags);
217
218 nblocked_emsgs_readers--;
219
220 if (n) {
221 spin_unlock_irqrestore(&emsgs_lock, flags);
222 return -ERESTARTSYS;
223 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 }
Ed L. Cashincf446f02008-02-08 04:20:03 -0800225 if (em->len > cnt) {
226 spin_unlock_irqrestore(&emsgs_lock, flags);
227 return -EAGAIN;
228 }
229 mp = em->msg;
230 len = em->len;
231 em->msg = NULL;
232 em->flags &= ~EMFL_VALID;
233
234 emsgs_head_idx++;
235 emsgs_head_idx %= ARRAY_SIZE(emsgs);
236
237 spin_unlock_irqrestore(&emsgs_lock, flags);
238
239 n = copy_to_user(buf, mp, len);
240 kfree(mp);
241 return n == 0 ? len : -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242}
243
Arjan van de Ven2b8693c2007-02-12 00:55:32 -0800244static const struct file_operations aoe_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .write = aoechr_write,
246 .read = aoechr_read,
247 .open = aoechr_open,
248 .release = aoechr_rel,
249 .owner = THIS_MODULE,
250};
251
252int __init
253aoechr_init(void)
254{
255 int n, i;
256
257 n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
258 if (n < 0) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400259 printk(KERN_ERR "aoe: can't register char device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 return n;
261 }
262 sema_init(&emsgs_sema, 0);
263 spin_lock_init(&emsgs_lock);
gregkh@suse.dedeb36972005-03-23 09:52:10 -0800264 aoe_class = class_create(THIS_MODULE, "aoe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 if (IS_ERR(aoe_class)) {
266 unregister_chrdev(AOE_MAJOR, "aoechr");
267 return PTR_ERR(aoe_class);
268 }
269 for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
Tony Jones7ea7ed02007-09-25 02:03:03 +0200270 device_create(aoe_class, NULL,
271 MKDEV(AOE_MAJOR, chardevs[i].minor), chardevs[i].name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273 return 0;
274}
275
276void
277aoechr_exit(void)
278{
279 int i;
280
281 for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
Tony Jones7ea7ed02007-09-25 02:03:03 +0200282 device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
gregkh@suse.dedeb36972005-03-23 09:52:10 -0800283 class_destroy(aoe_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 unregister_chrdev(AOE_MAJOR, "aoechr");
285}
286