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