xref: /linux/drivers/block/aoe/aoechr.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
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