Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *          An implementation of a loadable kernel mode driver providing
0004  *      multiple kernel/user space bidirectional communications links.
0005  *
0006  *      Author:     Alan Cox <alan@lxorguk.ukuu.org.uk>
0007  * 
0008  *              Adapted to become the Linux 2.0 Coda pseudo device
0009  *              Peter  Braam  <braam@maths.ox.ac.uk> 
0010  *              Michael Callahan <mjc@emmy.smith.edu>           
0011  *
0012  *              Changes for Linux 2.1
0013  *              Copyright (c) 1997 Carnegie-Mellon University
0014  */
0015 
0016 #include <linux/module.h>
0017 #include <linux/errno.h>
0018 #include <linux/kernel.h>
0019 #include <linux/major.h>
0020 #include <linux/time.h>
0021 #include <linux/sched/signal.h>
0022 #include <linux/slab.h>
0023 #include <linux/ioport.h>
0024 #include <linux/fcntl.h>
0025 #include <linux/delay.h>
0026 #include <linux/skbuff.h>
0027 #include <linux/proc_fs.h>
0028 #include <linux/vmalloc.h>
0029 #include <linux/fs.h>
0030 #include <linux/file.h>
0031 #include <linux/poll.h>
0032 #include <linux/init.h>
0033 #include <linux/list.h>
0034 #include <linux/mutex.h>
0035 #include <linux/device.h>
0036 #include <linux/pid_namespace.h>
0037 #include <asm/io.h>
0038 #include <linux/uaccess.h>
0039 
0040 #include <linux/coda.h>
0041 #include "coda_psdev.h"
0042 #include "coda_linux.h"
0043 
0044 #include "coda_int.h"
0045 
0046 /* statistics */
0047 int           coda_hard;         /* allows signals during upcalls */
0048 unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
0049 
0050 
0051 struct venus_comm coda_comms[MAX_CODADEVS];
0052 static struct class *coda_psdev_class;
0053 
0054 /*
0055  * Device operations
0056  */
0057 
0058 static __poll_t coda_psdev_poll(struct file *file, poll_table * wait)
0059 {
0060         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
0061     __poll_t mask = EPOLLOUT | EPOLLWRNORM;
0062 
0063     poll_wait(file, &vcp->vc_waitq, wait);
0064     mutex_lock(&vcp->vc_mutex);
0065     if (!list_empty(&vcp->vc_pending))
0066                 mask |= EPOLLIN | EPOLLRDNORM;
0067     mutex_unlock(&vcp->vc_mutex);
0068 
0069     return mask;
0070 }
0071 
0072 static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
0073 {
0074     unsigned int data;
0075 
0076     switch(cmd) {
0077     case CIOC_KERNEL_VERSION:
0078         data = CODA_KERNEL_VERSION;
0079         return put_user(data, (int __user *) arg);
0080     default:
0081         return -ENOTTY;
0082     }
0083 
0084     return 0;
0085 }
0086 
0087 /*
0088  *  Receive a message written by Venus to the psdev
0089  */
0090  
0091 static ssize_t coda_psdev_write(struct file *file, const char __user *buf, 
0092                 size_t nbytes, loff_t *off)
0093 {
0094         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
0095         struct upc_req *req = NULL;
0096         struct upc_req *tmp;
0097     struct list_head *lh;
0098     struct coda_in_hdr hdr;
0099     ssize_t retval = 0, count = 0;
0100     int error;
0101 
0102     /* make sure there is enough to copy out the (opcode, unique) values */
0103     if (nbytes < (2 * sizeof(u_int32_t)))
0104         return -EINVAL;
0105 
0106         /* Peek at the opcode, uniquefier */
0107     if (copy_from_user(&hdr, buf, 2 * sizeof(u_int32_t)))
0108             return -EFAULT;
0109 
0110         if (DOWNCALL(hdr.opcode)) {
0111         union outputArgs *dcbuf;
0112         int size = sizeof(*dcbuf);
0113 
0114         if  ( nbytes < sizeof(struct coda_out_hdr) ) {
0115             pr_warn("coda_downcall opc %d uniq %d, not enough!\n",
0116                 hdr.opcode, hdr.unique);
0117             count = nbytes;
0118             goto out;
0119         }
0120         if ( nbytes > size ) {
0121             pr_warn("downcall opc %d, uniq %d, too much!",
0122                 hdr.opcode, hdr.unique);
0123                 nbytes = size;
0124         }
0125 
0126         dcbuf = vmemdup_user(buf, nbytes);
0127         if (IS_ERR(dcbuf)) {
0128             retval = PTR_ERR(dcbuf);
0129             goto out;
0130         }
0131 
0132         /* what downcall errors does Venus handle ? */
0133         error = coda_downcall(vcp, hdr.opcode, dcbuf, nbytes);
0134 
0135         kvfree(dcbuf);
0136         if (error) {
0137             pr_warn("%s: coda_downcall error: %d\n",
0138                 __func__, error);
0139             retval = error;
0140             goto out;
0141         }
0142         count = nbytes;
0143         goto out;
0144     }
0145         
0146     /* Look for the message on the processing queue. */
0147     mutex_lock(&vcp->vc_mutex);
0148     list_for_each(lh, &vcp->vc_processing) {
0149         tmp = list_entry(lh, struct upc_req , uc_chain);
0150         if (tmp->uc_unique == hdr.unique) {
0151             req = tmp;
0152             list_del(&req->uc_chain);
0153             break;
0154         }
0155     }
0156     mutex_unlock(&vcp->vc_mutex);
0157 
0158     if (!req) {
0159         pr_warn("%s: msg (%d, %d) not found\n",
0160             __func__, hdr.opcode, hdr.unique);
0161         retval = -ESRCH;
0162         goto out;
0163     }
0164 
0165         /* move data into response buffer. */
0166     if (req->uc_outSize < nbytes) {
0167         pr_warn("%s: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
0168             __func__, req->uc_outSize, (long)nbytes,
0169             hdr.opcode, hdr.unique);
0170         nbytes = req->uc_outSize; /* don't have more space! */
0171     }
0172         if (copy_from_user(req->uc_data, buf, nbytes)) {
0173         req->uc_flags |= CODA_REQ_ABORT;
0174         wake_up(&req->uc_sleep);
0175         retval = -EFAULT;
0176         goto out;
0177     }
0178 
0179     /* adjust outsize. is this useful ?? */
0180     req->uc_outSize = nbytes;
0181     req->uc_flags |= CODA_REQ_WRITE;
0182     count = nbytes;
0183 
0184     /* Convert filedescriptor into a file handle */
0185     if (req->uc_opcode == CODA_OPEN_BY_FD) {
0186         struct coda_open_by_fd_out *outp =
0187             (struct coda_open_by_fd_out *)req->uc_data;
0188         if (!outp->oh.result) {
0189             outp->fh = fget(outp->fd);
0190             if (!outp->fh)
0191                 return -EBADF;
0192         }
0193     }
0194 
0195         wake_up(&req->uc_sleep);
0196 out:
0197         return(count ? count : retval);  
0198 }
0199 
0200 /*
0201  *  Read a message from the kernel to Venus
0202  */
0203 
0204 static ssize_t coda_psdev_read(struct file * file, char __user * buf, 
0205                    size_t nbytes, loff_t *off)
0206 {
0207     DECLARE_WAITQUEUE(wait, current);
0208         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
0209         struct upc_req *req;
0210     ssize_t retval = 0, count = 0;
0211 
0212     if (nbytes == 0)
0213         return 0;
0214 
0215     mutex_lock(&vcp->vc_mutex);
0216 
0217     add_wait_queue(&vcp->vc_waitq, &wait);
0218     set_current_state(TASK_INTERRUPTIBLE);
0219 
0220     while (list_empty(&vcp->vc_pending)) {
0221         if (file->f_flags & O_NONBLOCK) {
0222             retval = -EAGAIN;
0223             break;
0224         }
0225         if (signal_pending(current)) {
0226             retval = -ERESTARTSYS;
0227             break;
0228         }
0229         mutex_unlock(&vcp->vc_mutex);
0230         schedule();
0231         mutex_lock(&vcp->vc_mutex);
0232     }
0233 
0234     set_current_state(TASK_RUNNING);
0235     remove_wait_queue(&vcp->vc_waitq, &wait);
0236 
0237     if (retval)
0238         goto out;
0239 
0240     req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
0241     list_del(&req->uc_chain);
0242 
0243     /* Move the input args into userspace */
0244     count = req->uc_inSize;
0245     if (nbytes < req->uc_inSize) {
0246         pr_warn("%s: Venus read %ld bytes of %d in message\n",
0247             __func__, (long)nbytes, req->uc_inSize);
0248         count = nbytes;
0249         }
0250 
0251     if (copy_to_user(buf, req->uc_data, count))
0252             retval = -EFAULT;
0253         
0254     /* If request was not a signal, enqueue and don't free */
0255     if (!(req->uc_flags & CODA_REQ_ASYNC)) {
0256         req->uc_flags |= CODA_REQ_READ;
0257         list_add_tail(&(req->uc_chain), &vcp->vc_processing);
0258         goto out;
0259     }
0260 
0261     kvfree(req->uc_data);
0262     kfree(req);
0263 out:
0264     mutex_unlock(&vcp->vc_mutex);
0265     return (count ? count : retval);
0266 }
0267 
0268 static int coda_psdev_open(struct inode * inode, struct file * file)
0269 {
0270     struct venus_comm *vcp;
0271     int idx, err;
0272 
0273     if (task_active_pid_ns(current) != &init_pid_ns)
0274         return -EINVAL;
0275 
0276     if (current_user_ns() != &init_user_ns)
0277         return -EINVAL;
0278 
0279     idx = iminor(inode);
0280     if (idx < 0 || idx >= MAX_CODADEVS)
0281         return -ENODEV;
0282 
0283     err = -EBUSY;
0284     vcp = &coda_comms[idx];
0285     mutex_lock(&vcp->vc_mutex);
0286 
0287     if (!vcp->vc_inuse) {
0288         vcp->vc_inuse++;
0289 
0290         INIT_LIST_HEAD(&vcp->vc_pending);
0291         INIT_LIST_HEAD(&vcp->vc_processing);
0292         init_waitqueue_head(&vcp->vc_waitq);
0293         vcp->vc_sb = NULL;
0294         vcp->vc_seq = 0;
0295 
0296         file->private_data = vcp;
0297         err = 0;
0298     }
0299 
0300     mutex_unlock(&vcp->vc_mutex);
0301     return err;
0302 }
0303 
0304 
0305 static int coda_psdev_release(struct inode * inode, struct file * file)
0306 {
0307     struct venus_comm *vcp = (struct venus_comm *) file->private_data;
0308     struct upc_req *req, *tmp;
0309 
0310     if (!vcp || !vcp->vc_inuse ) {
0311         pr_warn("%s: Not open.\n", __func__);
0312         return -1;
0313     }
0314 
0315     mutex_lock(&vcp->vc_mutex);
0316 
0317     /* Wakeup clients so they can return. */
0318     list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
0319         list_del(&req->uc_chain);
0320 
0321         /* Async requests need to be freed here */
0322         if (req->uc_flags & CODA_REQ_ASYNC) {
0323             kvfree(req->uc_data);
0324             kfree(req);
0325             continue;
0326         }
0327         req->uc_flags |= CODA_REQ_ABORT;
0328         wake_up(&req->uc_sleep);
0329     }
0330 
0331     list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
0332         list_del(&req->uc_chain);
0333 
0334         req->uc_flags |= CODA_REQ_ABORT;
0335         wake_up(&req->uc_sleep);
0336     }
0337 
0338     file->private_data = NULL;
0339     vcp->vc_inuse--;
0340     mutex_unlock(&vcp->vc_mutex);
0341     return 0;
0342 }
0343 
0344 
0345 static const struct file_operations coda_psdev_fops = {
0346     .owner      = THIS_MODULE,
0347     .read       = coda_psdev_read,
0348     .write      = coda_psdev_write,
0349     .poll       = coda_psdev_poll,
0350     .unlocked_ioctl = coda_psdev_ioctl,
0351     .open       = coda_psdev_open,
0352     .release    = coda_psdev_release,
0353     .llseek     = noop_llseek,
0354 };
0355 
0356 static int __init init_coda_psdev(void)
0357 {
0358     int i, err = 0;
0359     if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
0360         pr_err("%s: unable to get major %d\n",
0361                __func__, CODA_PSDEV_MAJOR);
0362         return -EIO;
0363     }
0364     coda_psdev_class = class_create(THIS_MODULE, "coda");
0365     if (IS_ERR(coda_psdev_class)) {
0366         err = PTR_ERR(coda_psdev_class);
0367         goto out_chrdev;
0368     }       
0369     for (i = 0; i < MAX_CODADEVS; i++) {
0370         mutex_init(&(&coda_comms[i])->vc_mutex);
0371         device_create(coda_psdev_class, NULL,
0372                   MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i);
0373     }
0374     coda_sysctl_init();
0375     goto out;
0376 
0377 out_chrdev:
0378     unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
0379 out:
0380     return err;
0381 }
0382 
0383 MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
0384 MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
0385 MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
0386 MODULE_LICENSE("GPL");
0387 MODULE_VERSION("7.2");
0388 
0389 static int __init init_coda(void)
0390 {
0391     int status;
0392     int i;
0393 
0394     status = coda_init_inodecache();
0395     if (status)
0396         goto out2;
0397     status = init_coda_psdev();
0398     if ( status ) {
0399         pr_warn("Problem (%d) in init_coda_psdev\n", status);
0400         goto out1;
0401     }
0402     
0403     status = register_filesystem(&coda_fs_type);
0404     if (status) {
0405         pr_warn("failed to register filesystem!\n");
0406         goto out;
0407     }
0408     return 0;
0409 out:
0410     for (i = 0; i < MAX_CODADEVS; i++)
0411         device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
0412     class_destroy(coda_psdev_class);
0413     unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
0414     coda_sysctl_clean();
0415 out1:
0416     coda_destroy_inodecache();
0417 out2:
0418     return status;
0419 }
0420 
0421 static void __exit exit_coda(void)
0422 {
0423         int err, i;
0424 
0425     err = unregister_filesystem(&coda_fs_type);
0426     if (err != 0)
0427         pr_warn("failed to unregister filesystem\n");
0428     for (i = 0; i < MAX_CODADEVS; i++)
0429         device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
0430     class_destroy(coda_psdev_class);
0431     unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
0432     coda_sysctl_clean();
0433     coda_destroy_inodecache();
0434 }
0435 
0436 module_init(init_coda);
0437 module_exit(exit_coda);
0438