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