alistair23-linux/drivers/block/aoe/aoechr.c
Arjan van de Ven 2b8693c061 [PATCH] mark struct file_operations const 3
Many struct file_operations in the kernel can be "const".  Marking them const
moves these to the .rodata section, which avoids false sharing with potential
dirty data.  In addition it'll catch accidental writes at compile time to
these shared resources.

Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-02-12 09:48:45 -08:00

280 lines
5.2 KiB
C

/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoechr.c
* AoE character device driver
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include "aoe.h"
enum {
//MINOR_STAT = 1, (moved to sysfs)
MINOR_ERR = 2,
MINOR_DISCOVER,
MINOR_INTERFACES,
MINOR_REVALIDATE,
MSGSZ = 2048,
NMSG = 100, /* message backlog to retain */
};
struct aoe_chardev {
ulong minor;
char name[32];
};
enum { EMFL_VALID = 1 };
struct ErrMsg {
short flags;
short len;
char *msg;
};
static struct ErrMsg emsgs[NMSG];
static int emsgs_head_idx, emsgs_tail_idx;
static struct semaphore emsgs_sema;
static spinlock_t emsgs_lock;
static int nblocked_emsgs_readers;
static struct class *aoe_class;
static struct aoe_chardev chardevs[] = {
{ MINOR_ERR, "err" },
{ MINOR_DISCOVER, "discover" },
{ MINOR_INTERFACES, "interfaces" },
{ MINOR_REVALIDATE, "revalidate" },
};
static int
discover(void)
{
aoecmd_cfg(0xffff, 0xff);
return 0;
}
static int
interfaces(const char __user *str, size_t size)
{
if (set_aoe_iflist(str, size)) {
printk(KERN_ERR
"aoe: could not set interface list: too many interfaces\n");
return -EINVAL;
}
return 0;
}
static int
revalidate(const char __user *str, size_t size)
{
int major, minor, n;
ulong flags;
struct aoedev *d;
char buf[16];
if (size >= sizeof buf)
return -EINVAL;
buf[sizeof buf - 1] = '\0';
if (copy_from_user(buf, str, size))
return -EFAULT;
/* should be e%d.%d format */
n = sscanf(buf, "e%d.%d", &major, &minor);
if (n != 2) {
printk(KERN_ERR "aoe: invalid device specification\n");
return -EINVAL;
}
d = aoedev_by_aoeaddr(major, minor);
if (!d)
return -EINVAL;
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_MAXBCNT;
d->flags |= DEVFL_PAUSE;
spin_unlock_irqrestore(&d->lock, flags);
aoecmd_cfg(major, minor);
return 0;
}
void
aoechr_error(char *msg)
{
struct ErrMsg *em;
char *mp;
ulong flags, n;
n = strlen(msg);
spin_lock_irqsave(&emsgs_lock, flags);
em = emsgs + emsgs_tail_idx;
if ((em->flags & EMFL_VALID)) {
bail: spin_unlock_irqrestore(&emsgs_lock, flags);
return;
}
mp = kmalloc(n, GFP_ATOMIC);
if (mp == NULL) {
printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
goto bail;
}
memcpy(mp, msg, n);
em->msg = mp;
em->flags |= EMFL_VALID;
em->len = n;
emsgs_tail_idx++;
emsgs_tail_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
if (nblocked_emsgs_readers)
up(&emsgs_sema);
}
static ssize_t
aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
{
int ret = -EINVAL;
switch ((unsigned long) filp->private_data) {
default:
printk(KERN_INFO "aoe: can't write to that file.\n");
break;
case MINOR_DISCOVER:
ret = discover();
break;
case MINOR_INTERFACES:
ret = interfaces(buf, cnt);
break;
case MINOR_REVALIDATE:
ret = revalidate(buf, cnt);
}
if (ret == 0)
ret = cnt;
return ret;
}
static int
aoechr_open(struct inode *inode, struct file *filp)
{
int n, i;
n = iminor(inode);
filp->private_data = (void *) (unsigned long) n;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
if (chardevs[i].minor == n)
return 0;
return -EINVAL;
}
static int
aoechr_rel(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t
aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
unsigned long n;
char *mp;
struct ErrMsg *em;
ssize_t len;
ulong flags;
n = (unsigned long) filp->private_data;
switch (n) {
case MINOR_ERR:
spin_lock_irqsave(&emsgs_lock, flags);
loop:
em = emsgs + emsgs_head_idx;
if ((em->flags & EMFL_VALID) == 0) {
if (filp->f_flags & O_NDELAY) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
nblocked_emsgs_readers++;
spin_unlock_irqrestore(&emsgs_lock, flags);
n = down_interruptible(&emsgs_sema);
spin_lock_irqsave(&emsgs_lock, flags);
nblocked_emsgs_readers--;
if (n) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -ERESTARTSYS;
}
goto loop;
}
if (em->len > cnt) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
mp = em->msg;
len = em->len;
em->msg = NULL;
em->flags &= ~EMFL_VALID;
emsgs_head_idx++;
emsgs_head_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
n = copy_to_user(buf, mp, len);
kfree(mp);
return n == 0 ? len : -EFAULT;
default:
return -EFAULT;
}
}
static const struct file_operations aoe_fops = {
.write = aoechr_write,
.read = aoechr_read,
.open = aoechr_open,
.release = aoechr_rel,
.owner = THIS_MODULE,
};
int __init
aoechr_init(void)
{
int n, i;
n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
if (n < 0) {
printk(KERN_ERR "aoe: can't register char device\n");
return n;
}
sema_init(&emsgs_sema, 0);
spin_lock_init(&emsgs_lock);
aoe_class = class_create(THIS_MODULE, "aoe");
if (IS_ERR(aoe_class)) {
unregister_chrdev(AOE_MAJOR, "aoechr");
return PTR_ERR(aoe_class);
}
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
class_device_create(aoe_class, NULL,
MKDEV(AOE_MAJOR, chardevs[i].minor),
NULL, chardevs[i].name);
return 0;
}
void
aoechr_exit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
class_destroy(aoe_class);
unregister_chrdev(AOE_MAJOR, "aoechr");
}