1 /* 2 * An implementation of a loadable kernel mode driver providing 3 * multiple kernel/user space bidirectional communications links. 4 * 5 * Author: Alan Cox <alan@redhat.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Adapted to become the Linux 2.0 Coda pseudo device 13 * Peter Braam <braam@maths.ox.ac.uk> 14 * Michael Callahan <mjc@emmy.smith.edu> 15 * 16 * Changes for Linux 2.1 17 * Copyright (c) 1997 Carnegie-Mellon University 18 */ 19 20 #include <linux/module.h> 21 #include <linux/errno.h> 22 #include <linux/kernel.h> 23 #include <linux/major.h> 24 #include <linux/time.h> 25 #include <linux/slab.h> 26 #include <linux/ioport.h> 27 #include <linux/fcntl.h> 28 #include <linux/delay.h> 29 #include <linux/skbuff.h> 30 #include <linux/proc_fs.h> 31 #include <linux/devfs_fs_kernel.h> 32 #include <linux/vmalloc.h> 33 #include <linux/fs.h> 34 #include <linux/file.h> 35 #include <linux/poll.h> 36 #include <linux/init.h> 37 #include <linux/list.h> 38 #include <linux/smp_lock.h> 39 #include <linux/device.h> 40 #include <asm/io.h> 41 #include <asm/system.h> 42 #include <asm/poll.h> 43 #include <asm/uaccess.h> 44 45 #include <linux/coda.h> 46 #include <linux/coda_linux.h> 47 #include <linux/coda_fs_i.h> 48 #include <linux/coda_psdev.h> 49 #include <linux/coda_proc.h> 50 51 #define upc_free(r) kfree(r) 52 53 /* 54 * Coda stuff 55 */ 56 extern struct file_system_type coda_fs_type; 57 58 /* statistics */ 59 int coda_hard; /* allows signals during upcalls */ 60 unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ 61 62 63 struct venus_comm coda_comms[MAX_CODADEVS]; 64 static struct class_simple *coda_psdev_class; 65 66 /* 67 * Device operations 68 */ 69 70 static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) 71 { 72 struct venus_comm *vcp = (struct venus_comm *) file->private_data; 73 unsigned int mask = POLLOUT | POLLWRNORM; 74 75 poll_wait(file, &vcp->vc_waitq, wait); 76 if (!list_empty(&vcp->vc_pending)) 77 mask |= POLLIN | POLLRDNORM; 78 79 return mask; 80 } 81 82 static int coda_psdev_ioctl(struct inode * inode, struct file * filp, 83 unsigned int cmd, unsigned long arg) 84 { 85 unsigned int data; 86 87 switch(cmd) { 88 case CIOC_KERNEL_VERSION: 89 data = CODA_KERNEL_VERSION; 90 return put_user(data, (int __user *) arg); 91 default: 92 return -ENOTTY; 93 } 94 95 return 0; 96 } 97 98 /* 99 * Receive a message written by Venus to the psdev 100 */ 101 102 static ssize_t coda_psdev_write(struct file *file, const char __user *buf, 103 size_t nbytes, loff_t *off) 104 { 105 struct venus_comm *vcp = (struct venus_comm *) file->private_data; 106 struct upc_req *req = NULL; 107 struct upc_req *tmp; 108 struct list_head *lh; 109 struct coda_in_hdr hdr; 110 ssize_t retval = 0, count = 0; 111 int error; 112 113 /* Peek at the opcode, uniquefier */ 114 if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) 115 return -EFAULT; 116 117 if (DOWNCALL(hdr.opcode)) { 118 struct super_block *sb = NULL; 119 union outputArgs *dcbuf; 120 int size = sizeof(*dcbuf); 121 122 sb = vcp->vc_sb; 123 if ( !sb ) { 124 count = nbytes; 125 goto out; 126 } 127 128 if ( nbytes < sizeof(struct coda_out_hdr) ) { 129 printk("coda_downcall opc %d uniq %d, not enough!\n", 130 hdr.opcode, hdr.unique); 131 count = nbytes; 132 goto out; 133 } 134 if ( nbytes > size ) { 135 printk("Coda: downcall opc %d, uniq %d, too much!", 136 hdr.opcode, hdr.unique); 137 nbytes = size; 138 } 139 CODA_ALLOC(dcbuf, union outputArgs *, nbytes); 140 if (copy_from_user(dcbuf, buf, nbytes)) { 141 CODA_FREE(dcbuf, nbytes); 142 retval = -EFAULT; 143 goto out; 144 } 145 146 /* what downcall errors does Venus handle ? */ 147 lock_kernel(); 148 error = coda_downcall(hdr.opcode, dcbuf, sb); 149 unlock_kernel(); 150 151 CODA_FREE(dcbuf, nbytes); 152 if (error) { 153 printk("psdev_write: coda_downcall error: %d\n", error); 154 retval = error; 155 goto out; 156 } 157 count = nbytes; 158 goto out; 159 } 160 161 /* Look for the message on the processing queue. */ 162 lock_kernel(); 163 list_for_each(lh, &vcp->vc_processing) { 164 tmp = list_entry(lh, struct upc_req , uc_chain); 165 if (tmp->uc_unique == hdr.unique) { 166 req = tmp; 167 list_del(&req->uc_chain); 168 break; 169 } 170 } 171 unlock_kernel(); 172 173 if (!req) { 174 printk("psdev_write: msg (%d, %d) not found\n", 175 hdr.opcode, hdr.unique); 176 retval = -ESRCH; 177 goto out; 178 } 179 180 /* move data into response buffer. */ 181 if (req->uc_outSize < nbytes) { 182 printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n", 183 req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique); 184 nbytes = req->uc_outSize; /* don't have more space! */ 185 } 186 if (copy_from_user(req->uc_data, buf, nbytes)) { 187 req->uc_flags |= REQ_ABORT; 188 wake_up(&req->uc_sleep); 189 retval = -EFAULT; 190 goto out; 191 } 192 193 /* adjust outsize. is this useful ?? */ 194 req->uc_outSize = nbytes; 195 req->uc_flags |= REQ_WRITE; 196 count = nbytes; 197 198 /* Convert filedescriptor into a file handle */ 199 if (req->uc_opcode == CODA_OPEN_BY_FD) { 200 struct coda_open_by_fd_out *outp = 201 (struct coda_open_by_fd_out *)req->uc_data; 202 outp->fh = fget(outp->fd); 203 } 204 205 wake_up(&req->uc_sleep); 206 out: 207 return(count ? count : retval); 208 } 209 210 /* 211 * Read a message from the kernel to Venus 212 */ 213 214 static ssize_t coda_psdev_read(struct file * file, char __user * buf, 215 size_t nbytes, loff_t *off) 216 { 217 DECLARE_WAITQUEUE(wait, current); 218 struct venus_comm *vcp = (struct venus_comm *) file->private_data; 219 struct upc_req *req; 220 ssize_t retval = 0, count = 0; 221 222 if (nbytes == 0) 223 return 0; 224 225 lock_kernel(); 226 227 add_wait_queue(&vcp->vc_waitq, &wait); 228 set_current_state(TASK_INTERRUPTIBLE); 229 230 while (list_empty(&vcp->vc_pending)) { 231 if (file->f_flags & O_NONBLOCK) { 232 retval = -EAGAIN; 233 break; 234 } 235 if (signal_pending(current)) { 236 retval = -ERESTARTSYS; 237 break; 238 } 239 schedule(); 240 } 241 242 set_current_state(TASK_RUNNING); 243 remove_wait_queue(&vcp->vc_waitq, &wait); 244 245 if (retval) 246 goto out; 247 248 req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain); 249 list_del(&req->uc_chain); 250 251 /* Move the input args into userspace */ 252 count = req->uc_inSize; 253 if (nbytes < req->uc_inSize) { 254 printk ("psdev_read: Venus read %ld bytes of %d in message\n", 255 (long)nbytes, req->uc_inSize); 256 count = nbytes; 257 } 258 259 if (copy_to_user(buf, req->uc_data, count)) 260 retval = -EFAULT; 261 262 /* If request was not a signal, enqueue and don't free */ 263 if (!(req->uc_flags & REQ_ASYNC)) { 264 req->uc_flags |= REQ_READ; 265 list_add(&(req->uc_chain), vcp->vc_processing.prev); 266 goto out; 267 } 268 269 CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); 270 upc_free(req); 271 out: 272 unlock_kernel(); 273 return (count ? count : retval); 274 } 275 276 static int coda_psdev_open(struct inode * inode, struct file * file) 277 { 278 struct venus_comm *vcp; 279 int idx; 280 281 lock_kernel(); 282 idx = iminor(inode); 283 if(idx >= MAX_CODADEVS) { 284 unlock_kernel(); 285 return -ENODEV; 286 } 287 288 vcp = &coda_comms[idx]; 289 if(vcp->vc_inuse) { 290 unlock_kernel(); 291 return -EBUSY; 292 } 293 294 if (!vcp->vc_inuse++) { 295 INIT_LIST_HEAD(&vcp->vc_pending); 296 INIT_LIST_HEAD(&vcp->vc_processing); 297 init_waitqueue_head(&vcp->vc_waitq); 298 vcp->vc_sb = NULL; 299 vcp->vc_seq = 0; 300 } 301 302 file->private_data = vcp; 303 304 unlock_kernel(); 305 return 0; 306 } 307 308 309 static int coda_psdev_release(struct inode * inode, struct file * file) 310 { 311 struct venus_comm *vcp = (struct venus_comm *) file->private_data; 312 struct upc_req *req, *tmp; 313 314 lock_kernel(); 315 if ( !vcp->vc_inuse ) { 316 unlock_kernel(); 317 printk("psdev_release: Not open.\n"); 318 return -1; 319 } 320 321 if (--vcp->vc_inuse) { 322 unlock_kernel(); 323 return 0; 324 } 325 326 /* Wakeup clients so they can return. */ 327 list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) { 328 /* Async requests need to be freed here */ 329 if (req->uc_flags & REQ_ASYNC) { 330 CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); 331 upc_free(req); 332 continue; 333 } 334 req->uc_flags |= REQ_ABORT; 335 wake_up(&req->uc_sleep); 336 } 337 338 list_for_each_entry(req, &vcp->vc_processing, uc_chain) { 339 req->uc_flags |= REQ_ABORT; 340 wake_up(&req->uc_sleep); 341 } 342 343 unlock_kernel(); 344 return 0; 345 } 346 347 348 static struct file_operations coda_psdev_fops = { 349 .owner = THIS_MODULE, 350 .read = coda_psdev_read, 351 .write = coda_psdev_write, 352 .poll = coda_psdev_poll, 353 .ioctl = coda_psdev_ioctl, 354 .open = coda_psdev_open, 355 .release = coda_psdev_release, 356 }; 357 358 static int init_coda_psdev(void) 359 { 360 int i, err = 0; 361 if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) { 362 printk(KERN_ERR "coda_psdev: unable to get major %d\n", 363 CODA_PSDEV_MAJOR); 364 return -EIO; 365 } 366 coda_psdev_class = class_simple_create(THIS_MODULE, "coda"); 367 if (IS_ERR(coda_psdev_class)) { 368 err = PTR_ERR(coda_psdev_class); 369 goto out_chrdev; 370 } 371 devfs_mk_dir ("coda"); 372 for (i = 0; i < MAX_CODADEVS; i++) { 373 class_simple_device_add(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR,i), 374 NULL, "cfs%d", i); 375 err = devfs_mk_cdev(MKDEV(CODA_PSDEV_MAJOR, i), 376 S_IFCHR|S_IRUSR|S_IWUSR, "coda/%d", i); 377 if (err) 378 goto out_class; 379 } 380 coda_sysctl_init(); 381 goto out; 382 383 out_class: 384 for (i = 0; i < MAX_CODADEVS; i++) 385 class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); 386 class_simple_destroy(coda_psdev_class); 387 out_chrdev: 388 unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); 389 out: 390 return err; 391 } 392 393 394 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); 395 MODULE_LICENSE("GPL"); 396 397 extern int coda_init_inodecache(void); 398 extern void coda_destroy_inodecache(void); 399 static int __init init_coda(void) 400 { 401 int status; 402 int i; 403 printk(KERN_INFO "Coda Kernel/Venus communications, " 404 #ifdef CONFIG_CODA_FS_OLD_API 405 "v5.3.20" 406 #else 407 "v6.0.0" 408 #endif 409 ", coda@cs.cmu.edu\n"); 410 411 status = coda_init_inodecache(); 412 if (status) 413 goto out2; 414 status = init_coda_psdev(); 415 if ( status ) { 416 printk("Problem (%d) in init_coda_psdev\n", status); 417 goto out1; 418 } 419 420 status = register_filesystem(&coda_fs_type); 421 if (status) { 422 printk("coda: failed to register filesystem!\n"); 423 goto out; 424 } 425 return 0; 426 out: 427 for (i = 0; i < MAX_CODADEVS; i++) { 428 class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); 429 devfs_remove("coda/%d", i); 430 } 431 class_simple_destroy(coda_psdev_class); 432 devfs_remove("coda"); 433 unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); 434 coda_sysctl_clean(); 435 out1: 436 coda_destroy_inodecache(); 437 out2: 438 return status; 439 } 440 441 static void __exit exit_coda(void) 442 { 443 int err, i; 444 445 err = unregister_filesystem(&coda_fs_type); 446 if ( err != 0 ) { 447 printk("coda: failed to unregister filesystem\n"); 448 } 449 for (i = 0; i < MAX_CODADEVS; i++) { 450 class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); 451 devfs_remove("coda/%d", i); 452 } 453 class_simple_destroy(coda_psdev_class); 454 devfs_remove("coda"); 455 unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); 456 coda_sysctl_clean(); 457 coda_destroy_inodecache(); 458 } 459 460 module_init(init_coda); 461 module_exit(exit_coda); 462 463