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 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 721da177e4SLinus Torvalds discover(void) 731da177e4SLinus Torvalds { 741da177e4SLinus Torvalds aoecmd_cfg(0xffff, 0xff); 751da177e4SLinus Torvalds return 0; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static int 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 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 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 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 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 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 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 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 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