xref: /openbmc/linux/drivers/block/aoe/aoechr.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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;
52deb36970Sgregkh@suse.de 
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)611da177e4SLinus Torvalds static char *aoe_devnode(const struct device *dev, umode_t *mode)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds static const struct class aoe_class = {
671da177e4SLinus Torvalds 	.name = "aoe",
681da177e4SLinus Torvalds 	.devnode = aoe_devnode,
691da177e4SLinus Torvalds };
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds static int
discover(void)72a12c93f0SEd L. Cashin discover(void)
73a12c93f0SEd L. Cashin {
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)793ae1c24eSEd L. Cashin interfaces(const char __user *str, size_t size)
803ae1c24eSEd L. Cashin {
813ae1c24eSEd L. Cashin 	if (set_aoe_iflist(str, size)) {
823ae1c24eSEd L. Cashin 		printk(KERN_ERR
833ae1c24eSEd L. Cashin 			"aoe: could not set interface list: too many interfaces\n");
843ae1c24eSEd L. Cashin 		return -EINVAL;
8568e0d42fSEd L. Cashin 	}
863ae1c24eSEd L. Cashin 	return 0;
873ae1c24eSEd L. Cashin }
883ae1c24eSEd L. Cashin 
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;
953ae1c24eSEd L. Cashin 	struct sk_buff *skb;
96896831f5SEd Cashin 	char buf[16];
973ae1c24eSEd L. Cashin 
983ae1c24eSEd L. Cashin 	if (size >= sizeof buf)
990c966214SEd 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;
10368e0d42fSEd L. Cashin 
10425f4d75eSEd Cashin 	n = sscanf(buf, "e%d.%d", &major, &minor);
10568e0d42fSEd L. Cashin 	if (n != 2) {
10668e0d42fSEd L. Cashin 		pr_err("aoe: invalid device specification %s\n", buf);
1073ae1c24eSEd L. Cashin 		return -EINVAL;
10868e0d42fSEd L. Cashin 	}
10968e0d42fSEd L. Cashin 	d = aoedev_by_aoeaddr(major, minor, 0);
11068e0d42fSEd L. Cashin 	if (!d)
11125f4d75eSEd Cashin 		return -EINVAL;
11268e0d42fSEd L. Cashin 	spin_lock_irqsave(&d->lock, flags);
11368e0d42fSEd L. Cashin 	aoecmd_cleanslate(d);
11468e0d42fSEd L. Cashin 	aoecmd_cfg(major, minor);
11569cf2d85SEd Cashin loop:
116e9bb8fb0SDavid S. Miller 	skb = aoecmd_ata_id(d);
117e9bb8fb0SDavid S. Miller 	spin_unlock_irqrestore(&d->lock, flags);
118e9bb8fb0SDavid S. Miller 	/* try again if we are able to sleep a bit,
119e9bb8fb0SDavid S. Miller 	 * otherwise give up this revalidation
120e9bb8fb0SDavid S. Miller 	 */
121e9bb8fb0SDavid S. Miller 	if (!skb && !msleep_interruptible(250)) {
1223ae1c24eSEd L. Cashin 		spin_lock_irqsave(&d->lock, flags);
1233ae1c24eSEd L. Cashin 		goto loop;
1243ae1c24eSEd L. Cashin 	}
1251da177e4SLinus Torvalds 	aoedev_put(d);
1261da177e4SLinus Torvalds 	if (skb) {
1271da177e4SLinus Torvalds 		struct sk_buff_head queue;
1281da177e4SLinus Torvalds 		__skb_queue_head_init(&queue);
1291da177e4SLinus Torvalds 		__skb_queue_tail(&queue, skb);
1301da177e4SLinus Torvalds 		aoenet_xmit(&queue);
1311da177e4SLinus Torvalds 	}
1321da177e4SLinus Torvalds 	return 0;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
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 
14260abc786SMihnea Dobrescu-Balaur 	n = strlen(msg);
14376cdb09bSZhen Lei 
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 
1521da177e4SLinus Torvalds 	mp = kmemdup(msg, n, GFP_ATOMIC);
1531da177e4SLinus Torvalds 	if (!mp)
1541da177e4SLinus Torvalds 		goto bail;
1551da177e4SLinus Torvalds 
15624879a8eSMatthias Kaehlcke 	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)
166a12c93f0SEd L. Cashin 		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 
1743ae1c24eSEd L. Cashin 	switch ((unsigned long) filp->private_data) {
1753ae1c24eSEd L. Cashin 	default:
176262bf541SEd L. Cashin 		printk(KERN_INFO "aoe: can't write to that file.\n");
177262bf541SEd L. Cashin 		break;
178262bf541SEd L. Cashin 	case MINOR_DISCOVER:
179b21faa25SEd Cashin 		ret = discover();
1801da177e4SLinus Torvalds 		break;
1811da177e4SLinus Torvalds 	case MINOR_INTERFACES:
1821da177e4SLinus Torvalds 		ret = interfaces(buf, cnt);
1831da177e4SLinus Torvalds 		break;
1841da177e4SLinus Torvalds 	case MINOR_REVALIDATE:
1851da177e4SLinus Torvalds 		ret = revalidate(buf, cnt);
1861da177e4SLinus Torvalds 		break;
1871da177e4SLinus Torvalds 	case MINOR_FLUSH:
1881da177e4SLinus Torvalds 		ret = aoedev_flush(buf, cnt);
1891da177e4SLinus Torvalds 		break;
1901da177e4SLinus Torvalds 	}
1912a48fc0aSArnd Bergmann 	if (ret == 0)
1922017b376SEric Sesterhenn 		ret = cnt;
1931da177e4SLinus Torvalds 	return ret;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
196579174a5SJonathan Corbet static int
aoechr_open(struct inode * inode,struct file * filp)1972a48fc0aSArnd Bergmann aoechr_open(struct inode *inode, struct file *filp)
1981da177e4SLinus Torvalds {
199579174a5SJonathan Corbet 	int n, i;
2002a48fc0aSArnd Bergmann 
2011da177e4SLinus Torvalds 	mutex_lock(&aoechr_mutex);
2021da177e4SLinus Torvalds 	n = iminor(inode);
2031da177e4SLinus Torvalds 	filp->private_data = (void *) (unsigned long) n;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
2061da177e4SLinus Torvalds 		if (chardevs[i].minor == n) {
2071da177e4SLinus Torvalds 			mutex_unlock(&aoechr_mutex);
2081da177e4SLinus Torvalds 			return 0;
2091da177e4SLinus Torvalds 		}
2101da177e4SLinus Torvalds 	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 
220cf446f0dSEd L. Cashin static ssize_t
aoechr_read(struct file * filp,char __user * buf,size_t cnt,loff_t * off)221cf446f0dSEd L. Cashin aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
222cf446f0dSEd L. Cashin {
2231da177e4SLinus Torvalds 	unsigned long n;
224cf446f0dSEd L. Cashin 	char *mp;
225cf446f0dSEd L. Cashin 	struct ErrMsg *em;
2261da177e4SLinus Torvalds 	ssize_t len;
227cf446f0dSEd L. Cashin 	ulong flags;
228cf446f0dSEd L. Cashin 
2291da177e4SLinus Torvalds 	n = (unsigned long) filp->private_data;
2301da177e4SLinus Torvalds 	if (n != MINOR_ERR)
2311da177e4SLinus Torvalds 		return -EFAULT;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	spin_lock_irqsave(&emsgs_lock, flags);
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	for (;;) {
2361da177e4SLinus Torvalds 		em = emsgs + emsgs_head_idx;
23724879a8eSMatthias Kaehlcke 		if ((em->flags & EMFL_VALID) != 0)
2381da177e4SLinus Torvalds 			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 
2471da177e4SLinus Torvalds 		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 
2672b8693c0SArjan van de Ven 	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);
2736038f373SArnd Bergmann 	kfree(mp);
2741da177e4SLinus Torvalds 	return n == 0 ? len : -EFAULT;
2751da177e4SLinus Torvalds }
276ff62b8e6SGreg Kroah-Hartman 
2771ce8a0d3SKay Sievers static const struct file_operations aoe_fops = {
2781ce8a0d3SKay Sievers 	.write = aoechr_write,
2791ce8a0d3SKay Sievers 	.read = aoechr_read,
2801ce8a0d3SKay Sievers 	.open = aoechr_open,
2811da177e4SLinus Torvalds 	.release = aoechr_rel,
2821da177e4SLinus Torvalds 	.owner = THIS_MODULE,
2831da177e4SLinus Torvalds 	.llseek = noop_llseek,
2841da177e4SLinus Torvalds };
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds int __init
aoechr_init(void)2871da177e4SLinus Torvalds aoechr_init(void)
288a12c93f0SEd L. Cashin {
2891da177e4SLinus Torvalds 	int n, i;
2901da177e4SLinus Torvalds 
29124879a8eSMatthias Kaehlcke 	n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
2921da177e4SLinus Torvalds 	if (n < 0) {
293*1aaba11dSGreg Kroah-Hartman 		printk(KERN_ERR "aoe: can't register char device\n");
2941da177e4SLinus Torvalds 		return n;
2951da177e4SLinus Torvalds 	}
2961da177e4SLinus Torvalds 	init_completion(&emsgs_comp);
2971da177e4SLinus Torvalds 	spin_lock_init(&emsgs_lock);
298e454cea2SKay Sievers 	n = class_register(&aoe_class);
2991ce8a0d3SKay Sievers 	if (n) {
3001da177e4SLinus Torvalds 		unregister_chrdev(AOE_MAJOR, "aoechr");
3011ff9f542SGreg Kroah-Hartman 		return n;
3021ff9f542SGreg Kroah-Hartman 	}
3031ff9f542SGreg Kroah-Hartman 
3041da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
3051da177e4SLinus Torvalds 		device_create(&aoe_class, NULL,
3061da177e4SLinus Torvalds 			      MKDEV(AOE_MAJOR, chardevs[i].minor), NULL,
3071da177e4SLinus Torvalds 			      chardevs[i].name);
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	return 0;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds void
aoechr_exit(void)3131da177e4SLinus Torvalds aoechr_exit(void)
3147ea7ed01STony Jones {
315deb36970Sgregkh@suse.de 	int i;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
3181da177e4SLinus Torvalds 		device_destroy(&aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
319 	class_unregister(&aoe_class);
320 	unregister_chrdev(AOE_MAJOR, "aoechr");
321 }
322 
323