aoechr.c revision 6bb6285fdb948cedee586c6bebc9ebc5e32a5c35
1/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */ 2/* 3 * aoechr.c 4 * AoE character device driver 5 */ 6 7#include <linux/hdreg.h> 8#include <linux/blkdev.h> 9#include "aoe.h" 10 11enum { 12 //MINOR_STAT = 1, (moved to sysfs) 13 MINOR_ERR = 2, 14 MINOR_DISCOVER, 15 MINOR_INTERFACES, 16 MINOR_REVALIDATE, 17 MSGSZ = 2048, 18 NMSG = 100, /* message backlog to retain */ 19}; 20 21struct aoe_chardev { 22 ulong minor; 23 char name[32]; 24}; 25 26enum { EMFL_VALID = 1 }; 27 28struct ErrMsg { 29 short flags; 30 short len; 31 char *msg; 32}; 33 34static struct ErrMsg emsgs[NMSG]; 35static int emsgs_head_idx, emsgs_tail_idx; 36static struct semaphore emsgs_sema; 37static spinlock_t emsgs_lock; 38static int nblocked_emsgs_readers; 39static struct class *aoe_class; 40static struct aoe_chardev chardevs[] = { 41 { MINOR_ERR, "err" }, 42 { MINOR_DISCOVER, "discover" }, 43 { MINOR_INTERFACES, "interfaces" }, 44 { MINOR_REVALIDATE, "revalidate" }, 45}; 46 47static int 48discover(void) 49{ 50 aoecmd_cfg(0xffff, 0xff); 51 return 0; 52} 53 54static int 55interfaces(const char __user *str, size_t size) 56{ 57 if (set_aoe_iflist(str, size)) { 58 eprintk("could not set interface list: too many interfaces\n"); 59 return -EINVAL; 60 } 61 return 0; 62} 63 64static int 65revalidate(const char __user *str, size_t size) 66{ 67 int major, minor, n; 68 ulong flags; 69 struct aoedev *d; 70 char buf[16]; 71 72 if (size >= sizeof buf) 73 return -EINVAL; 74 buf[sizeof buf - 1] = '\0'; 75 if (copy_from_user(buf, str, size)) 76 return -EFAULT; 77 78 /* should be e%d.%d format */ 79 n = sscanf(buf, "e%d.%d", &major, &minor); 80 if (n != 2) { 81 eprintk("invalid device specification\n"); 82 return -EINVAL; 83 } 84 d = aoedev_by_aoeaddr(major, minor); 85 if (!d) 86 return -EINVAL; 87 88 spin_lock_irqsave(&d->lock, flags); 89 d->flags &= ~DEVFL_MAXBCNT; 90 d->flags |= DEVFL_PAUSE; 91 spin_unlock_irqrestore(&d->lock, flags); 92 aoecmd_cfg(major, minor); 93 94 return 0; 95} 96 97void 98aoechr_error(char *msg) 99{ 100 struct ErrMsg *em; 101 char *mp; 102 ulong flags, n; 103 104 n = strlen(msg); 105 106 spin_lock_irqsave(&emsgs_lock, flags); 107 108 em = emsgs + emsgs_tail_idx; 109 if ((em->flags & EMFL_VALID)) { 110bail: spin_unlock_irqrestore(&emsgs_lock, flags); 111 return; 112 } 113 114 mp = kmalloc(n, GFP_ATOMIC); 115 if (mp == NULL) { 116 eprintk("allocation failure, len=%ld\n", n); 117 goto bail; 118 } 119 120 memcpy(mp, msg, n); 121 em->msg = mp; 122 em->flags |= EMFL_VALID; 123 em->len = n; 124 125 emsgs_tail_idx++; 126 emsgs_tail_idx %= ARRAY_SIZE(emsgs); 127 128 spin_unlock_irqrestore(&emsgs_lock, flags); 129 130 if (nblocked_emsgs_readers) 131 up(&emsgs_sema); 132} 133 134static ssize_t 135aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp) 136{ 137 int ret = -EINVAL; 138 139 switch ((unsigned long) filp->private_data) { 140 default: 141 iprintk("can't write to that file.\n"); 142 break; 143 case MINOR_DISCOVER: 144 ret = discover(); 145 break; 146 case MINOR_INTERFACES: 147 ret = interfaces(buf, cnt); 148 break; 149 case MINOR_REVALIDATE: 150 ret = revalidate(buf, cnt); 151 } 152 if (ret == 0) 153 ret = cnt; 154 return ret; 155} 156 157static int 158aoechr_open(struct inode *inode, struct file *filp) 159{ 160 int n, i; 161 162 n = iminor(inode); 163 filp->private_data = (void *) (unsigned long) n; 164 165 for (i = 0; i < ARRAY_SIZE(chardevs); ++i) 166 if (chardevs[i].minor == n) 167 return 0; 168 return -EINVAL; 169} 170 171static int 172aoechr_rel(struct inode *inode, struct file *filp) 173{ 174 return 0; 175} 176 177static ssize_t 178aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) 179{ 180 unsigned long n; 181 char *mp; 182 struct ErrMsg *em; 183 ssize_t len; 184 ulong flags; 185 186 n = (unsigned long) filp->private_data; 187 switch (n) { 188 case MINOR_ERR: 189 spin_lock_irqsave(&emsgs_lock, flags); 190loop: 191 em = emsgs + emsgs_head_idx; 192 if ((em->flags & EMFL_VALID) == 0) { 193 if (filp->f_flags & O_NDELAY) { 194 spin_unlock_irqrestore(&emsgs_lock, flags); 195 return -EAGAIN; 196 } 197 nblocked_emsgs_readers++; 198 199 spin_unlock_irqrestore(&emsgs_lock, flags); 200 201 n = down_interruptible(&emsgs_sema); 202 203 spin_lock_irqsave(&emsgs_lock, flags); 204 205 nblocked_emsgs_readers--; 206 207 if (n) { 208 spin_unlock_irqrestore(&emsgs_lock, flags); 209 return -ERESTARTSYS; 210 } 211 goto loop; 212 } 213 if (em->len > cnt) { 214 spin_unlock_irqrestore(&emsgs_lock, flags); 215 return -EAGAIN; 216 } 217 mp = em->msg; 218 len = em->len; 219 em->msg = NULL; 220 em->flags &= ~EMFL_VALID; 221 222 emsgs_head_idx++; 223 emsgs_head_idx %= ARRAY_SIZE(emsgs); 224 225 spin_unlock_irqrestore(&emsgs_lock, flags); 226 227 n = copy_to_user(buf, mp, len); 228 kfree(mp); 229 return n == 0 ? len : -EFAULT; 230 default: 231 return -EFAULT; 232 } 233} 234 235static struct file_operations aoe_fops = { 236 .write = aoechr_write, 237 .read = aoechr_read, 238 .open = aoechr_open, 239 .release = aoechr_rel, 240 .owner = THIS_MODULE, 241}; 242 243int __init 244aoechr_init(void) 245{ 246 int n, i; 247 248 n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops); 249 if (n < 0) { 250 eprintk("can't register char device\n"); 251 return n; 252 } 253 sema_init(&emsgs_sema, 0); 254 spin_lock_init(&emsgs_lock); 255 aoe_class = class_create(THIS_MODULE, "aoe"); 256 if (IS_ERR(aoe_class)) { 257 unregister_chrdev(AOE_MAJOR, "aoechr"); 258 return PTR_ERR(aoe_class); 259 } 260 for (i = 0; i < ARRAY_SIZE(chardevs); ++i) 261 class_device_create(aoe_class, NULL, 262 MKDEV(AOE_MAJOR, chardevs[i].minor), 263 NULL, chardevs[i].name); 264 265 return 0; 266} 267 268void 269aoechr_exit(void) 270{ 271 int i; 272 273 for (i = 0; i < ARRAY_SIZE(chardevs); ++i) 274 class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor)); 275 class_destroy(aoe_class); 276 unregister_chrdev(AOE_MAJOR, "aoechr"); 277} 278 279