📄 psdev.c
字号:
/* * An implementation of a loadable kernel mode driver providing * multiple kernel/user space bidirectional communications links. * * Author: Alan Cox <alan@cymru.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Adapted to become the Linux 2.0 Coda pseudo device * Peter Braam <braam@maths.ox.ac.uk> * Michael Callahan <mjc@emmy.smith.edu> * * Changes for Linux 2.1 * Copyright (c) 1997 Carnegie-Mellon University * * Redone again for InterMezzo * Copyright (c) 1998 Peter J. Braam * Copyright (c) 2000 Mountain View Data, Inc. * Copyright (c) 2000 Tacitus Systems, Inc. * Copyright (c) 2001 Cluster File Systems, Inc. * * Extended attribute support * Copyright (c) 2001 Shirish. H. Phatak * Copyright (c) 2001 Tacit Networks, Inc. */#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/lp.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/fcntl.h>#include <linux/delay.h>#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/list.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/system.h>#include <asm/poll.h>#include <asm/uaccess.h>#include <linux/intermezzo_fs.h>#include <linux/intermezzo_upcall.h>#include <linux/intermezzo_psdev.h>#include <linux/intermezzo_kml.h>#ifdef PRESTO_DEVELint presto_print_entry = 1;int presto_debug = 4095;#elseint presto_print_entry = 0;int presto_debug = 0;#endif/* Like inode.c (presto_sym_iops), the initializer is just to prevent upc_comms from appearing as a COMMON symbol (and therefore interfering with other modules that use the same variable name. */struct upc_comm upc_comms[MAX_PRESTODEV] = {{0}};/* * Device operations: map file to upcall structure */static inline struct upc_comm *presto_psdev_f2u(struct file *file){ int minor; if ( MAJOR(file->f_dentry->d_inode->i_rdev) != PRESTO_PSDEV_MAJOR ) { EXIT; return NULL; } minor = MINOR(file->f_dentry->d_inode->i_rdev); if ( minor < 0 || minor >= MAX_PRESTODEV ) { EXIT; return NULL; } return &(upc_comms[minor]);}inline int presto_lento_up(int minor) { return upc_comms[minor].uc_pid;}static unsigned int presto_psdev_poll(struct file *file, poll_table * wait){ struct upc_comm *upccom; unsigned int mask = POLLOUT | POLLWRNORM; /* ENTRY; this will flood you */ if ( ! (upccom = presto_psdev_f2u(file)) ) { kdev_t dev = file->f_dentry->d_inode->i_rdev; printk("InterMezzo: %s, bad device %s\n", __FUNCTION__, kdevname(dev)); } poll_wait(file, &(upccom->uc_waitq), wait); if (!list_empty(&upccom->uc_pending)) { CDEBUG(D_PSDEV, "Non-empty pending list.\n"); mask |= POLLIN | POLLRDNORM; } /* EXIT; will flood you */ return mask;}/* * Receive a message written by Lento to the psdev */static ssize_t presto_psdev_write(struct file *file, const char *buf, size_t count, loff_t *off){ struct upc_comm *upccom; struct upc_req *req = NULL; struct upc_req *tmp; struct list_head *lh; struct lento_down_hdr hdr; int error; if ( ! (upccom = presto_psdev_f2u(file)) ) { kdev_t dev = file->f_dentry->d_inode->i_rdev; printk("InterMezzo: %s, bad device %s\n", __FUNCTION__, kdevname(dev)); } /* Peek at the opcode, uniquefier */ if ( count < sizeof(hdr) ) { printk("presto_psdev_write: Lento didn't write full hdr.\n"); return -EINVAL; } error = copy_from_user(&hdr, buf, sizeof(hdr)); if ( error ) return error; CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%d,%d)\n", current->pid, hdr.opcode, hdr.unique); /* Look for the message on the processing queue. */ lh = &upccom->uc_processing; while ( (lh = lh->next) != &upccom->uc_processing ) { tmp = list_entry(lh, struct upc_req , rq_chain); if (tmp->rq_unique == hdr.unique) { req = tmp; /* unlink here: keeps search length minimal */ list_del(&req->rq_chain); INIT_LIST_HEAD(&req->rq_chain); CDEBUG(D_PSDEV,"Eureka opc %d uniq %d!\n", hdr.opcode, hdr.unique); break; } } if (!req) { printk("psdev_write: msg (%d, %d) not found\n", hdr.opcode, hdr.unique); return(-ESRCH); } /* move data into response buffer. */ if (req->rq_bufsize < count) { printk("psdev_write: too much cnt: %d, cnt: %Zd, " "opc: %d, uniq: %d.\n", req->rq_bufsize, count, hdr.opcode, hdr.unique); count = req->rq_bufsize; /* don't have more space! */ } error = copy_from_user(req->rq_data, buf, count); if ( error ) return error; /* adjust outsize: good upcalls can be aware of this */ req->rq_rep_size = count; req->rq_flags |= REQ_WRITE; wake_up(&req->rq_sleep); return(count);}/* * Read a message from the kernel to Lento */static ssize_t presto_psdev_read(struct file * file, char * buf, size_t count, loff_t *off){ struct upc_comm *upccom; struct upc_req *req; int result = count; if ( ! (upccom = presto_psdev_f2u(file)) ) { kdev_t dev = file->f_dentry->d_inode->i_rdev; printk("InterMezzo: %s, bad device %s\n", __FUNCTION__, kdevname(dev)); } CDEBUG(D_PSDEV, "count %Zd\n", count); if (list_empty(&(upccom->uc_pending))) { CDEBUG(D_UPCALL, "Empty pending list in read, not good\n"); return -EINVAL; } req = list_entry((upccom->uc_pending.next), struct upc_req, rq_chain); list_del(&(req->rq_chain)); if (! (req->rq_flags & REQ_ASYNC) ) { list_add(&(req->rq_chain), upccom->uc_processing.prev); } req->rq_flags |= REQ_READ; /* Move the input args into userspace */ if (req->rq_bufsize <= count) { result = req->rq_bufsize; } if (count < req->rq_bufsize) { printk ("psdev_read: buffer too small, read %Zd of %d bytes\n", count, req->rq_bufsize); } if ( copy_to_user(buf, req->rq_data, result) ) { return -EFAULT; } /* If request was asynchronous don't enqueue, but free */ if (req->rq_flags & REQ_ASYNC) { CDEBUG(D_PSDEV, "psdev_read: async msg (%d, %d), result %d\n", req->rq_opcode, req->rq_unique, result); PRESTO_FREE(req->rq_data, req->rq_bufsize); PRESTO_FREE(req, sizeof(*req)); return result; } return result;}static int presto_psdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct upc_comm *upccom; /* XXX is this rdev or dev? */ kdev_t dev = inode->i_rdev; ENTRY; upccom = presto_psdev_f2u(file); if ( !upccom) { printk("InterMezzo: %s, bad device %s\n", __FUNCTION__, kdevname(dev)); EXIT; return -ENODEV; } switch(cmd) { case TCGETS: return -EINVAL; case PRESTO_GETMOUNT: { /* return all the mounts for this device. */ int minor = 0; int len, outlen; struct readmount readmount; struct readmount *user_readmount = (struct readmount *) arg; char * tmp; int error; error = copy_from_user(&readmount, (void *)arg, sizeof(readmount)); if ( error ) { printk("psdev: can't copy %Zd bytes from %p to %p\n", sizeof(readmount), (struct readmount *) arg, &readmount); EXIT; return error; } len = readmount.io_len; minor = MINOR(dev); PRESTO_ALLOC(tmp, char *, len); if (!tmp) { EXIT; return -ENOMEM; } outlen = presto_sprint_mounts(tmp, len, minor); CDEBUG(D_PSDEV, "presto_sprint_mounts returns %d bytes\n", outlen); /* as this came out on 1/3/2000, it could NEVER work. * So fix it ... RGM * I mean, let's let the compiler do a little work ... * gcc suggested the extra () */ error = copy_to_user(readmount.io_string, tmp, outlen); if ( error ) { CDEBUG(D_PSDEV, "Copy_to_user string 0x%p failed\n", readmount.io_string); } if ((!error) && (error = copy_to_user(&(user_readmount->io_len), &outlen, sizeof(int))) ) { CDEBUG(D_PSDEV, "Copy_to_user len @0x%p failed\n", &(user_readmount->io_len)); } PRESTO_FREE(tmp, len); EXIT; return error; } case PRESTO_SETPID: { /* * This ioctl is performed by each Lento that starts up * and wants to do further communication with presto. */ CDEBUG(D_PSDEV, "Setting current pid to %d\n", current->pid); upccom->uc_pid = current->pid; if ( !list_empty(&upccom->uc_processing) ) { struct list_head *lh; struct upc_req *req; printk("WARNING: setpid & processing not empty!\n"); lh = &upccom->uc_processing; while ( (lh = lh->next) != &upccom->uc_processing) { req = list_entry(lh, struct upc_req, rq_chain); /* freeing of req and data is done by the sleeper */ wake_up(&req->rq_sleep); } } if ( !list_empty(&upccom->uc_processing) ) { printk("BAD: FAILDED TO CLEAN PROCESSING LIST!\n"); } EXIT; return 0; } case PRESTO_CLEAR_FSETROOT: { /* * Close KML files. */ int error; int saved_pid = upccom->uc_pid; char *path; struct { char *path; int path_len; } input; error = copy_from_user(&input, (char *)arg, sizeof(input)); if ( error ) { EXIT; return error; } PRESTO_ALLOC(path, char *, input.path_len + 1); if ( !path ) { EXIT; return -ENOMEM; } error = copy_from_user(path, input.path, input.path_len); if ( error ) { PRESTO_FREE(path, input.path_len + 1); EXIT; return error; } path[input.path_len] = '\0'; CDEBUG(D_PSDEV, "clear_fsetroot: path %s\n", path); upccom->uc_pid = current->pid; error = presto_clear_fsetroot(path); upccom->uc_pid = saved_pid; PRESTO_FREE(path, input.path_len + 1); EXIT; return error; } case PRESTO_CLEAR_ALL_FSETROOTS: { /* * Close KML files. */ int error; int saved_pid = upccom->uc_pid; char *path; struct { char *path; int path_len; } input; error = copy_from_user(&input, (char *)arg, sizeof(input)); if ( error ) { EXIT; return error; } PRESTO_ALLOC(path, char *, input.path_len + 1); if ( !path ) { EXIT; return -ENOMEM; } error = copy_from_user(path, input.path, input.path_len); if ( error ) { PRESTO_FREE(path, input.path_len + 1); EXIT; return error;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -