152e112b3ab6b2b35a144565c8ea3bdda1e2845f2Ed L. Cashin/* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * aoechr.c
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * AoE character device driver
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/hdreg.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkdev.h>
924879a8e3e68f146d4d85528cc0b5dea712b77c5Matthias Kaehlcke#include <linux/completion.h>
1068e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin#include <linux/delay.h>
115a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
122a48fc0ab24241755dc93bfd4f01d68efab47f5aArnd Bergmann#include <linux/mutex.h>
13e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller#include <linux/skbuff.h>
14d5decd3b9512e35c87492312a72443192eebdda9Paul Gortmaker#include <linux/export.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "aoe.h"
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum {
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	//MINOR_STAT = 1, (moved to sysfs)
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	MINOR_ERR = 2,
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	MINOR_DISCOVER,
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	MINOR_INTERFACES,
223ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	MINOR_REVALIDATE,
23262bf54144ebcb78cd0d057d2705dc5fb7bba7acEd L. Cashin	MINOR_FLUSH,
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	MSGSZ = 2048,
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NMSG = 100,		/* message backlog to retain */
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct aoe_chardev {
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong minor;
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char name[32];
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum { EMFL_VALID = 1 };
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ErrMsg {
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short flags;
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short len;
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *msg;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
412a48fc0ab24241755dc93bfd4f01d68efab47f5aArnd Bergmannstatic DEFINE_MUTEX(aoechr_mutex);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ErrMsg emsgs[NMSG];
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int emsgs_head_idx, emsgs_tail_idx;
4424879a8e3e68f146d4d85528cc0b5dea712b77c5Matthias Kaehlckestatic struct completion emsgs_comp;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic spinlock_t emsgs_lock;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int nblocked_emsgs_readers;
47deb3697037a7d362d13468a73643e09cbc1615a8Greg Kroah-Hartmanstatic struct class *aoe_class;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct aoe_chardev chardevs[] = {
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ MINOR_ERR, "err" },
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ MINOR_DISCOVER, "discover" },
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ MINOR_INTERFACES, "interfaces" },
523ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	{ MINOR_REVALIDATE, "revalidate" },
53262bf54144ebcb78cd0d057d2705dc5fb7bba7acEd L. Cashin	{ MINOR_FLUSH, "flush" },
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdiscover(void)
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	aoecmd_cfg(0xffff, 0xff);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsinterfaces(const char __user *str, size_t size)
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (set_aoe_iflist(str, size)) {
67a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_ERR
68a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin			"aoe: could not set interface list: too many interfaces\n");
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
743ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashinstatic int
753ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashinrevalidate(const char __user *str, size_t size)
763ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin{
773ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	int major, minor, n;
783ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	ulong flags;
793ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	struct aoedev *d;
8068e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	struct sk_buff *skb;
813ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	char buf[16];
823ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin
833ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	if (size >= sizeof buf)
843ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin		return -EINVAL;
853ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	buf[sizeof buf - 1] = '\0';
863ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	if (copy_from_user(buf, str, size))
873ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin		return -EFAULT;
883ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin
893ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	/* should be e%d.%d format */
903ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	n = sscanf(buf, "e%d.%d", &major, &minor);
913ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	if (n != 2) {
92a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_ERR "aoe: invalid device specification\n");
933ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin		return -EINVAL;
943ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	}
953ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	d = aoedev_by_aoeaddr(major, minor);
963ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	if (!d)
973ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin		return -EINVAL;
983ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	spin_lock_irqsave(&d->lock, flags);
9968e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	aoecmd_cleanslate(d);
10068e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashinloop:
10168e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	skb = aoecmd_ata_id(d);
1023ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	spin_unlock_irqrestore(&d->lock, flags);
10368e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	/* try again if we are able to sleep a bit,
10468e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	 * otherwise give up this revalidation
10568e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	 */
10668e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	if (!skb && !msleep_interruptible(200)) {
10768e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin		spin_lock_irqsave(&d->lock, flags);
10868e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin		goto loop;
10968e0d42f39d85b334d3867a4e5fc2e0e775c1a6cEd L. Cashin	}
110e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller	if (skb) {
111e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller		struct sk_buff_head queue;
112e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller		__skb_queue_head_init(&queue);
113e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller		__skb_queue_tail(&queue, skb);
114e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller		aoenet_xmit(&queue);
115e9bb8fb0b6d61a822201537b25206a0ca34b9d1dDavid S. Miller	}
1163ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	aoecmd_cfg(major, minor);
1173ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	return 0;
1183ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin}
1193ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_error(char *msg)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ErrMsg *em;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *mp;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong flags, n;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	n = strlen(msg);
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&emsgs_lock, flags);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	em = emsgs + emsgs_tail_idx;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((em->flags & EMFL_VALID)) {
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsbail:		spin_unlock_irqrestore(&emsgs_lock, flags);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp = kmalloc(n, GFP_ATOMIC);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mp == NULL) {
139a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto bail;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(mp, msg, n);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	em->msg = mp;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	em->flags |= EMFL_VALID;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	em->len = n;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	emsgs_tail_idx++;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	emsgs_tail_idx %= ARRAY_SIZE(emsgs);
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&emsgs_lock, flags);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (nblocked_emsgs_readers)
15424879a8e3e68f146d4d85528cc0b5dea712b77c5Matthias Kaehlcke		complete(&emsgs_comp);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -EINVAL;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch ((unsigned long) filp->private_data) {
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
164a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_INFO "aoe: can't write to that file.\n");
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MINOR_DISCOVER:
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = discover();
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MINOR_INTERFACES:
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = interfaces(buf, cnt);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1723ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin	case MINOR_REVALIDATE:
1733ae1c24e395b2b65326439622223d88d92bfa03aEd L. Cashin		ret = revalidate(buf, cnt);
174262bf54144ebcb78cd0d057d2705dc5fb7bba7acEd L. Cashin		break;
175262bf54144ebcb78cd0d057d2705dc5fb7bba7acEd L. Cashin	case MINOR_FLUSH:
176262bf54144ebcb78cd0d057d2705dc5fb7bba7acEd L. Cashin		ret = aoedev_flush(buf, cnt);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret == 0)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = cnt;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_open(struct inode *inode, struct file *filp)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int n, i;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1882a48fc0ab24241755dc93bfd4f01d68efab47f5aArnd Bergmann	mutex_lock(&aoechr_mutex);
1892017b376c01a54bf7d26ad4f461abe9b5f9064feEric Sesterhenn	n = iminor(inode);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	filp->private_data = (void *) (unsigned long) n;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
193579174a55f491edeaccb8f5d3dc7ad69a17f5423Jonathan Corbet		if (chardevs[i].minor == n) {
1942a48fc0ab24241755dc93bfd4f01d68efab47f5aArnd Bergmann			mutex_unlock(&aoechr_mutex);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
196579174a55f491edeaccb8f5d3dc7ad69a17f5423Jonathan Corbet		}
1972a48fc0ab24241755dc93bfd4f01d68efab47f5aArnd Bergmann	mutex_unlock(&aoechr_mutex);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_rel(struct inode *inode, struct file *filp)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long n;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *mp;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ErrMsg *em;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t len;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong flags;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	n = (unsigned long) filp->private_data;
217cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	if (n != MINOR_ERR)
218cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		return -EFAULT;
219cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin
220cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	spin_lock_irqsave(&emsgs_lock, flags);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
222cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	for (;;) {
223cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		em = emsgs + emsgs_head_idx;
224cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		if ((em->flags & EMFL_VALID) != 0)
225cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin			break;
226cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		if (filp->f_flags & O_NDELAY) {
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock_irqrestore(&emsgs_lock, flags);
228cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin			return -EAGAIN;
229cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		}
230cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		nblocked_emsgs_readers++;
231cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin
232cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		spin_unlock_irqrestore(&emsgs_lock, flags);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23424879a8e3e68f146d4d85528cc0b5dea712b77c5Matthias Kaehlcke		n = wait_for_completion_interruptible(&emsgs_comp);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
236cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		spin_lock_irqsave(&emsgs_lock, flags);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
238cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		nblocked_emsgs_readers--;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
240cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		if (n) {
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock_irqrestore(&emsgs_lock, flags);
242cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin			return -ERESTARTSYS;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
244cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	}
245cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	if (em->len > cnt) {
246cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		spin_unlock_irqrestore(&emsgs_lock, flags);
247cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin		return -EAGAIN;
248cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	}
249cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	mp = em->msg;
250cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	len = em->len;
251cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	em->msg = NULL;
252cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	em->flags &= ~EMFL_VALID;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
254cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	emsgs_head_idx++;
255cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	emsgs_head_idx %= ARRAY_SIZE(emsgs);
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
257cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	spin_unlock_irqrestore(&emsgs_lock, flags);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
259cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	n = copy_to_user(buf, mp, len);
260cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	kfree(mp);
261cf446f0dbafb5428a551da1c0df8f56316831df8Ed L. Cashin	return n == 0 ? len : -EFAULT;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2642b8693c0617e972fc0b2fd1ebf8de97e15b656c3Arjan van de Venstatic const struct file_operations aoe_fops = {
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = aoechr_write,
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read = aoechr_read,
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open = aoechr_open,
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = aoechr_rel,
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner = THIS_MODULE,
2706038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2732c9ede55ecec58099b72e4bb8eab719f32f72c31Al Virostatic char *aoe_devnode(struct device *dev, umode_t *mode)
2741ce8a0d396288f28070483a8190843c23b8282f4Kay Sievers{
2751ce8a0d396288f28070483a8190843c23b8282f4Kay Sievers	return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
2761ce8a0d396288f28070483a8190843c23b8282f4Kay Sievers}
2771ce8a0d396288f28070483a8190843c23b8282f4Kay Sievers
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint __init
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_init(void)
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int n, i;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (n < 0) {
285a12c93f08b8fc83b7fcdabaf92b1adcea7489f5eEd L. Cashin		printk(KERN_ERR "aoe: can't register char device\n");
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return n;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
28824879a8e3e68f146d4d85528cc0b5dea712b77c5Matthias Kaehlcke	init_completion(&emsgs_comp);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&emsgs_lock);
290deb3697037a7d362d13468a73643e09cbc1615a8Greg Kroah-Hartman	aoe_class = class_create(THIS_MODULE, "aoe");
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(aoe_class)) {
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unregister_chrdev(AOE_MAJOR, "aoechr");
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return PTR_ERR(aoe_class);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
295e454cea20bdcff10ee698d11b8882662a0153a47Kay Sievers	aoe_class->devnode = aoe_devnode;
2961ce8a0d396288f28070483a8190843c23b8282f4Kay Sievers
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
2981ff9f542e5f87c299226557ce5e67a402ed4b502Greg Kroah-Hartman		device_create(aoe_class, NULL,
2991ff9f542e5f87c299226557ce5e67a402ed4b502Greg Kroah-Hartman			      MKDEV(AOE_MAJOR, chardevs[i].minor), NULL,
3001ff9f542e5f87c299226557ce5e67a402ed4b502Greg Kroah-Hartman			      chardevs[i].name);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsaoechr_exit(void)
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
3117ea7ed01ff741918532978b30f6f226ed6f78476Tony Jones		device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
312deb3697037a7d362d13468a73643e09cbc1615a8Greg Kroah-Hartman	class_destroy(aoe_class);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_chrdev(AOE_MAJOR, "aoechr");
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
316