1fea05a26SEd Cashin /* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * aoechr.c
41da177e4SLinus Torvalds * AoE character device driver
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/hdreg.h>
81da177e4SLinus Torvalds #include <linux/blkdev.h>
924879a8eSMatthias Kaehlcke #include <linux/completion.h>
1068e0d42fSEd L. Cashin #include <linux/delay.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
122a48fc0aSArnd Bergmann #include <linux/mutex.h>
13e9bb8fb0SDavid S. Miller #include <linux/skbuff.h>
14d5decd3bSPaul Gortmaker #include <linux/export.h>
151da177e4SLinus Torvalds #include "aoe.h"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds enum {
181da177e4SLinus Torvalds //MINOR_STAT = 1, (moved to sysfs)
191da177e4SLinus Torvalds MINOR_ERR = 2,
201da177e4SLinus Torvalds MINOR_DISCOVER,
211da177e4SLinus Torvalds MINOR_INTERFACES,
223ae1c24eSEd L. Cashin MINOR_REVALIDATE,
23262bf541SEd L. Cashin MINOR_FLUSH,
241da177e4SLinus Torvalds MSGSZ = 2048,
251da177e4SLinus Torvalds NMSG = 100, /* message backlog to retain */
261da177e4SLinus Torvalds };
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds struct aoe_chardev {
291da177e4SLinus Torvalds ulong minor;
301da177e4SLinus Torvalds char name[32];
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds enum { EMFL_VALID = 1 };
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds struct ErrMsg {
361da177e4SLinus Torvalds short flags;
371da177e4SLinus Torvalds short len;
381da177e4SLinus Torvalds char *msg;
391da177e4SLinus Torvalds };
401da177e4SLinus Torvalds
412a48fc0aSArnd Bergmann static DEFINE_MUTEX(aoechr_mutex);
42662a8896SEd Cashin
43662a8896SEd Cashin /* A ring buffer of error messages, to be read through
44662a8896SEd Cashin * "/dev/etherd/err". When no messages are present,
45662a8896SEd Cashin * readers will block waiting for messages to appear.
46662a8896SEd Cashin */
471da177e4SLinus Torvalds static struct ErrMsg emsgs[NMSG];
481da177e4SLinus Torvalds static int emsgs_head_idx, emsgs_tail_idx;
4924879a8eSMatthias Kaehlcke static struct completion emsgs_comp;
501da177e4SLinus Torvalds static spinlock_t emsgs_lock;
511da177e4SLinus Torvalds static int nblocked_emsgs_readers;
5265d7a37dSIvan Orlov
531da177e4SLinus Torvalds static struct aoe_chardev chardevs[] = {
541da177e4SLinus Torvalds { MINOR_ERR, "err" },
551da177e4SLinus Torvalds { MINOR_DISCOVER, "discover" },
561da177e4SLinus Torvalds { MINOR_INTERFACES, "interfaces" },
573ae1c24eSEd L. Cashin { MINOR_REVALIDATE, "revalidate" },
58262bf541SEd L. Cashin { MINOR_FLUSH, "flush" },
591da177e4SLinus Torvalds };
601da177e4SLinus Torvalds
aoe_devnode(const struct device * dev,umode_t * mode)6165d7a37dSIvan Orlov static char *aoe_devnode(const struct device *dev, umode_t *mode)
6265d7a37dSIvan Orlov {
6365d7a37dSIvan Orlov return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
6465d7a37dSIvan Orlov }
6565d7a37dSIvan Orlov
6665d7a37dSIvan Orlov static const struct class aoe_class = {
6765d7a37dSIvan Orlov .name = "aoe",
6865d7a37dSIvan Orlov .devnode = aoe_devnode,
6965d7a37dSIvan Orlov };
7065d7a37dSIvan Orlov
711da177e4SLinus Torvalds static int
discover(void)721da177e4SLinus Torvalds discover(void)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds aoecmd_cfg(0xffff, 0xff);
751da177e4SLinus Torvalds return 0;
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds
781da177e4SLinus Torvalds static int
interfaces(const char __user * str,size_t size)791da177e4SLinus Torvalds interfaces(const char __user *str, size_t size)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds if (set_aoe_iflist(str, size)) {
82a12c93f0SEd L. Cashin printk(KERN_ERR
83a12c93f0SEd L. Cashin "aoe: could not set interface list: too many interfaces\n");
841da177e4SLinus Torvalds return -EINVAL;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds return 0;
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds
893ae1c24eSEd L. Cashin static int
revalidate(const char __user * str,size_t size)903ae1c24eSEd L. Cashin revalidate(const char __user *str, size_t size)
913ae1c24eSEd L. Cashin {
923ae1c24eSEd L. Cashin int major, minor, n;
933ae1c24eSEd L. Cashin ulong flags;
943ae1c24eSEd L. Cashin struct aoedev *d;
9568e0d42fSEd L. Cashin struct sk_buff *skb;
963ae1c24eSEd L. Cashin char buf[16];
973ae1c24eSEd L. Cashin
983ae1c24eSEd L. Cashin if (size >= sizeof buf)
993ae1c24eSEd L. Cashin return -EINVAL;
1003ae1c24eSEd L. Cashin buf[sizeof buf - 1] = '\0';
1013ae1c24eSEd L. Cashin if (copy_from_user(buf, str, size))
1023ae1c24eSEd L. Cashin return -EFAULT;
1033ae1c24eSEd L. Cashin
1043ae1c24eSEd L. Cashin n = sscanf(buf, "e%d.%d", &major, &minor);
1053ae1c24eSEd L. Cashin if (n != 2) {
106896831f5SEd Cashin pr_err("aoe: invalid device specification %s\n", buf);
1073ae1c24eSEd L. Cashin return -EINVAL;
1083ae1c24eSEd L. Cashin }
1090c966214SEd Cashin d = aoedev_by_aoeaddr(major, minor, 0);
1103ae1c24eSEd L. Cashin if (!d)
1113ae1c24eSEd L. Cashin return -EINVAL;
1123ae1c24eSEd L. Cashin spin_lock_irqsave(&d->lock, flags);
11368e0d42fSEd L. Cashin aoecmd_cleanslate(d);
11425f4d75eSEd Cashin aoecmd_cfg(major, minor);
11568e0d42fSEd L. Cashin loop:
11668e0d42fSEd L. Cashin skb = aoecmd_ata_id(d);
1173ae1c24eSEd L. Cashin spin_unlock_irqrestore(&d->lock, flags);
11868e0d42fSEd L. Cashin /* try again if we are able to sleep a bit,
11968e0d42fSEd L. Cashin * otherwise give up this revalidation
12068e0d42fSEd L. Cashin */
12125f4d75eSEd Cashin if (!skb && !msleep_interruptible(250)) {
12268e0d42fSEd L. Cashin spin_lock_irqsave(&d->lock, flags);
12368e0d42fSEd L. Cashin goto loop;
12468e0d42fSEd L. Cashin }
12569cf2d85SEd Cashin aoedev_put(d);
126e9bb8fb0SDavid S. Miller if (skb) {
127e9bb8fb0SDavid S. Miller struct sk_buff_head queue;
128e9bb8fb0SDavid S. Miller __skb_queue_head_init(&queue);
129e9bb8fb0SDavid S. Miller __skb_queue_tail(&queue, skb);
130e9bb8fb0SDavid S. Miller aoenet_xmit(&queue);
131e9bb8fb0SDavid S. Miller }
1323ae1c24eSEd L. Cashin return 0;
1333ae1c24eSEd L. Cashin }
1343ae1c24eSEd L. Cashin
1351da177e4SLinus Torvalds void
aoechr_error(char * msg)1361da177e4SLinus Torvalds aoechr_error(char *msg)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds struct ErrMsg *em;
1391da177e4SLinus Torvalds char *mp;
1401da177e4SLinus Torvalds ulong flags, n;
1411da177e4SLinus Torvalds
1421da177e4SLinus Torvalds n = strlen(msg);
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds spin_lock_irqsave(&emsgs_lock, flags);
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds em = emsgs + emsgs_tail_idx;
1471da177e4SLinus Torvalds if ((em->flags & EMFL_VALID)) {
1481da177e4SLinus Torvalds bail: spin_unlock_irqrestore(&emsgs_lock, flags);
1491da177e4SLinus Torvalds return;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds
15260abc786SMihnea Dobrescu-Balaur mp = kmemdup(msg, n, GFP_ATOMIC);
15376cdb09bSZhen Lei if (!mp)
1541da177e4SLinus Torvalds goto bail;
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds em->msg = mp;
1571da177e4SLinus Torvalds em->flags |= EMFL_VALID;
1581da177e4SLinus Torvalds em->len = n;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds emsgs_tail_idx++;
1611da177e4SLinus Torvalds emsgs_tail_idx %= ARRAY_SIZE(emsgs);
1621da177e4SLinus Torvalds
1631da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds if (nblocked_emsgs_readers)
16624879a8eSMatthias Kaehlcke complete(&emsgs_comp);
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds
1691da177e4SLinus Torvalds static ssize_t
aoechr_write(struct file * filp,const char __user * buf,size_t cnt,loff_t * offp)1701da177e4SLinus Torvalds aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds int ret = -EINVAL;
1731da177e4SLinus Torvalds
1741da177e4SLinus Torvalds switch ((unsigned long) filp->private_data) {
1751da177e4SLinus Torvalds default:
176a12c93f0SEd L. Cashin printk(KERN_INFO "aoe: can't write to that file.\n");
1771da177e4SLinus Torvalds break;
1781da177e4SLinus Torvalds case MINOR_DISCOVER:
1791da177e4SLinus Torvalds ret = discover();
1801da177e4SLinus Torvalds break;
1811da177e4SLinus Torvalds case MINOR_INTERFACES:
1821da177e4SLinus Torvalds ret = interfaces(buf, cnt);
1831da177e4SLinus Torvalds break;
1843ae1c24eSEd L. Cashin case MINOR_REVALIDATE:
1853ae1c24eSEd L. Cashin ret = revalidate(buf, cnt);
186262bf541SEd L. Cashin break;
187262bf541SEd L. Cashin case MINOR_FLUSH:
188262bf541SEd L. Cashin ret = aoedev_flush(buf, cnt);
189b21faa25SEd Cashin break;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds if (ret == 0)
1921da177e4SLinus Torvalds ret = cnt;
1931da177e4SLinus Torvalds return ret;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds
1961da177e4SLinus Torvalds static int
aoechr_open(struct inode * inode,struct file * filp)1971da177e4SLinus Torvalds aoechr_open(struct inode *inode, struct file *filp)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds int n, i;
2001da177e4SLinus Torvalds
2012a48fc0aSArnd Bergmann mutex_lock(&aoechr_mutex);
2022017b376SEric Sesterhenn n = iminor(inode);
2031da177e4SLinus Torvalds filp->private_data = (void *) (unsigned long) n;
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
206579174a5SJonathan Corbet if (chardevs[i].minor == n) {
2072a48fc0aSArnd Bergmann mutex_unlock(&aoechr_mutex);
2081da177e4SLinus Torvalds return 0;
209579174a5SJonathan Corbet }
2102a48fc0aSArnd Bergmann mutex_unlock(&aoechr_mutex);
2111da177e4SLinus Torvalds return -EINVAL;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds
2141da177e4SLinus Torvalds static int
aoechr_rel(struct inode * inode,struct file * filp)2151da177e4SLinus Torvalds aoechr_rel(struct inode *inode, struct file *filp)
2161da177e4SLinus Torvalds {
2171da177e4SLinus Torvalds return 0;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
2201da177e4SLinus Torvalds static ssize_t
aoechr_read(struct file * filp,char __user * buf,size_t cnt,loff_t * off)2211da177e4SLinus Torvalds aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds unsigned long n;
2241da177e4SLinus Torvalds char *mp;
2251da177e4SLinus Torvalds struct ErrMsg *em;
2261da177e4SLinus Torvalds ssize_t len;
2271da177e4SLinus Torvalds ulong flags;
2281da177e4SLinus Torvalds
2291da177e4SLinus Torvalds n = (unsigned long) filp->private_data;
230cf446f0dSEd L. Cashin if (n != MINOR_ERR)
231cf446f0dSEd L. Cashin return -EFAULT;
232cf446f0dSEd L. Cashin
2331da177e4SLinus Torvalds spin_lock_irqsave(&emsgs_lock, flags);
234cf446f0dSEd L. Cashin
235cf446f0dSEd L. Cashin for (;;) {
2361da177e4SLinus Torvalds em = emsgs + emsgs_head_idx;
237cf446f0dSEd L. Cashin if ((em->flags & EMFL_VALID) != 0)
238cf446f0dSEd L. Cashin break;
2391da177e4SLinus Torvalds if (filp->f_flags & O_NDELAY) {
2401da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
2411da177e4SLinus Torvalds return -EAGAIN;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds nblocked_emsgs_readers++;
2441da177e4SLinus Torvalds
2451da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
2461da177e4SLinus Torvalds
24724879a8eSMatthias Kaehlcke n = wait_for_completion_interruptible(&emsgs_comp);
2481da177e4SLinus Torvalds
2491da177e4SLinus Torvalds spin_lock_irqsave(&emsgs_lock, flags);
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds nblocked_emsgs_readers--;
2521da177e4SLinus Torvalds
2531da177e4SLinus Torvalds if (n) {
2541da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
2551da177e4SLinus Torvalds return -ERESTARTSYS;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds if (em->len > cnt) {
2591da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
2601da177e4SLinus Torvalds return -EAGAIN;
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds mp = em->msg;
2631da177e4SLinus Torvalds len = em->len;
2641da177e4SLinus Torvalds em->msg = NULL;
2651da177e4SLinus Torvalds em->flags &= ~EMFL_VALID;
2661da177e4SLinus Torvalds
2671da177e4SLinus Torvalds emsgs_head_idx++;
2681da177e4SLinus Torvalds emsgs_head_idx %= ARRAY_SIZE(emsgs);
2691da177e4SLinus Torvalds
2701da177e4SLinus Torvalds spin_unlock_irqrestore(&emsgs_lock, flags);
2711da177e4SLinus Torvalds
2721da177e4SLinus Torvalds n = copy_to_user(buf, mp, len);
2731da177e4SLinus Torvalds kfree(mp);
2741da177e4SLinus Torvalds return n == 0 ? len : -EFAULT;
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds
2772b8693c0SArjan van de Ven static const struct file_operations aoe_fops = {
2781da177e4SLinus Torvalds .write = aoechr_write,
2791da177e4SLinus Torvalds .read = aoechr_read,
2801da177e4SLinus Torvalds .open = aoechr_open,
2811da177e4SLinus Torvalds .release = aoechr_rel,
2821da177e4SLinus Torvalds .owner = THIS_MODULE,
2836038f373SArnd Bergmann .llseek = noop_llseek,
2841da177e4SLinus Torvalds };
2851da177e4SLinus Torvalds
2861da177e4SLinus Torvalds int __init
aoechr_init(void)2871da177e4SLinus Torvalds aoechr_init(void)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds int n, i;
2901da177e4SLinus Torvalds
2911da177e4SLinus Torvalds n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
2921da177e4SLinus Torvalds if (n < 0) {
293a12c93f0SEd L. Cashin printk(KERN_ERR "aoe: can't register char device\n");
2941da177e4SLinus Torvalds return n;
2951da177e4SLinus Torvalds }
29624879a8eSMatthias Kaehlcke init_completion(&emsgs_comp);
2971da177e4SLinus Torvalds spin_lock_init(&emsgs_lock);
29865d7a37dSIvan Orlov n = class_register(&aoe_class);
29965d7a37dSIvan Orlov if (n) {
3001da177e4SLinus Torvalds unregister_chrdev(AOE_MAJOR, "aoechr");
30165d7a37dSIvan Orlov return n;
3021da177e4SLinus Torvalds }
3031ce8a0d3SKay Sievers
3041da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
30565d7a37dSIvan Orlov device_create(&aoe_class, NULL,
3061ff9f542SGreg Kroah-Hartman MKDEV(AOE_MAJOR, chardevs[i].minor), NULL,
3071ff9f542SGreg Kroah-Hartman chardevs[i].name);
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds return 0;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds
3121da177e4SLinus Torvalds void
aoechr_exit(void)3131da177e4SLinus Torvalds aoechr_exit(void)
3141da177e4SLinus Torvalds {
3151da177e4SLinus Torvalds int i;
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
31865d7a37dSIvan Orlov device_destroy(&aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
31965d7a37dSIvan Orlov class_unregister(&aoe_class);
3201da177e4SLinus Torvalds unregister_chrdev(AOE_MAJOR, "aoechr");
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds
323