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