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