12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * An implementation of a loadable kernel mode driver providing
41da177e4SLinus Torvalds * multiple kernel/user space bidirectional communications links.
51da177e4SLinus Torvalds *
6526719baSAlan Cox * Author: Alan Cox <alan@lxorguk.ukuu.org.uk>
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * Adapted to become the Linux 2.0 Coda pseudo device
91da177e4SLinus Torvalds * Peter Braam <braam@maths.ox.ac.uk>
101da177e4SLinus Torvalds * Michael Callahan <mjc@emmy.smith.edu>
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * Changes for Linux 2.1
131da177e4SLinus Torvalds * Copyright (c) 1997 Carnegie-Mellon University
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/errno.h>
181da177e4SLinus Torvalds #include <linux/kernel.h>
191da177e4SLinus Torvalds #include <linux/major.h>
201da177e4SLinus Torvalds #include <linux/time.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
221da177e4SLinus Torvalds #include <linux/slab.h>
231da177e4SLinus Torvalds #include <linux/ioport.h>
241da177e4SLinus Torvalds #include <linux/fcntl.h>
251da177e4SLinus Torvalds #include <linux/delay.h>
261da177e4SLinus Torvalds #include <linux/skbuff.h>
271da177e4SLinus Torvalds #include <linux/proc_fs.h>
281da177e4SLinus Torvalds #include <linux/vmalloc.h>
291da177e4SLinus Torvalds #include <linux/fs.h>
301da177e4SLinus Torvalds #include <linux/file.h>
311da177e4SLinus Torvalds #include <linux/poll.h>
321da177e4SLinus Torvalds #include <linux/init.h>
331da177e4SLinus Torvalds #include <linux/list.h>
34da47c19eSYoshihisa Abe #include <linux/mutex.h>
351da177e4SLinus Torvalds #include <linux/device.h>
369fd973e0SEric W. Biederman #include <linux/pid_namespace.h>
371da177e4SLinus Torvalds #include <asm/io.h>
38834b46c3SFabian Frederick #include <linux/uaccess.h>
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds #include <linux/coda.h>
418fc8b9dfSDavid Howells #include "coda_psdev.h"
4231a203dfSAl Viro #include "coda_linux.h"
4331a203dfSAl Viro
44c98d8cfbSAdrian Bunk #include "coda_int.h"
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds /* statistics */
471da177e4SLinus Torvalds int coda_hard; /* allows signals during upcalls */
481da177e4SLinus Torvalds unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds struct venus_comm coda_comms[MAX_CODADEVS];
521db560afSgregkh@suse.de static struct class *coda_psdev_class;
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds /*
551da177e4SLinus Torvalds * Device operations
561da177e4SLinus Torvalds */
571da177e4SLinus Torvalds
coda_psdev_poll(struct file * file,poll_table * wait)58076ccb76SAl Viro static __poll_t coda_psdev_poll(struct file *file, poll_table * wait)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds struct venus_comm *vcp = (struct venus_comm *) file->private_data;
61a9a08845SLinus Torvalds __poll_t mask = EPOLLOUT | EPOLLWRNORM;
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds poll_wait(file, &vcp->vc_waitq, wait);
64da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
651da177e4SLinus Torvalds if (!list_empty(&vcp->vc_pending))
66a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
67da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
681da177e4SLinus Torvalds
691da177e4SLinus Torvalds return mask;
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds
coda_psdev_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)7297718390SArnd Bergmann static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds unsigned int data;
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds switch(cmd) {
771da177e4SLinus Torvalds case CIOC_KERNEL_VERSION:
781da177e4SLinus Torvalds data = CODA_KERNEL_VERSION;
791da177e4SLinus Torvalds return put_user(data, (int __user *) arg);
801da177e4SLinus Torvalds default:
811da177e4SLinus Torvalds return -ENOTTY;
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds return 0;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds
871da177e4SLinus Torvalds /*
881da177e4SLinus Torvalds * Receive a message written by Venus to the psdev
891da177e4SLinus Torvalds */
901da177e4SLinus Torvalds
coda_psdev_write(struct file * file,const char __user * buf,size_t nbytes,loff_t * off)911da177e4SLinus Torvalds static ssize_t coda_psdev_write(struct file *file, const char __user *buf,
921da177e4SLinus Torvalds size_t nbytes, loff_t *off)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds struct venus_comm *vcp = (struct venus_comm *) file->private_data;
951da177e4SLinus Torvalds struct upc_req *req = NULL;
961da177e4SLinus Torvalds struct upc_req *tmp;
971da177e4SLinus Torvalds struct list_head *lh;
981da177e4SLinus Torvalds struct coda_in_hdr hdr;
991da177e4SLinus Torvalds ssize_t retval = 0, count = 0;
1001da177e4SLinus Torvalds int error;
1011da177e4SLinus Torvalds
1026e51f8aaSJan Harkes /* make sure there is enough to copy out the (opcode, unique) values */
1036e51f8aaSJan Harkes if (nbytes < (2 * sizeof(u_int32_t)))
1046e51f8aaSJan Harkes return -EINVAL;
1056e51f8aaSJan Harkes
1061da177e4SLinus Torvalds /* Peek at the opcode, uniquefier */
1076e51f8aaSJan Harkes if (copy_from_user(&hdr, buf, 2 * sizeof(u_int32_t)))
1081da177e4SLinus Torvalds return -EFAULT;
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds if (DOWNCALL(hdr.opcode)) {
1111da177e4SLinus Torvalds union outputArgs *dcbuf;
1121da177e4SLinus Torvalds int size = sizeof(*dcbuf);
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds if ( nbytes < sizeof(struct coda_out_hdr) ) {
115d9b4b319SFabian Frederick pr_warn("coda_downcall opc %d uniq %d, not enough!\n",
1161da177e4SLinus Torvalds hdr.opcode, hdr.unique);
1171da177e4SLinus Torvalds count = nbytes;
1181da177e4SLinus Torvalds goto out;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds if ( nbytes > size ) {
121f38cfb25SFabian Frederick pr_warn("downcall opc %d, uniq %d, too much!",
1221da177e4SLinus Torvalds hdr.opcode, hdr.unique);
1231da177e4SLinus Torvalds nbytes = size;
1241da177e4SLinus Torvalds }
125118b7ee1SJing Yangyang
126118b7ee1SJing Yangyang dcbuf = vmemdup_user(buf, nbytes);
127118b7ee1SJing Yangyang if (IS_ERR(dcbuf)) {
128118b7ee1SJing Yangyang retval = PTR_ERR(dcbuf);
1291da177e4SLinus Torvalds goto out;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds
1321da177e4SLinus Torvalds /* what downcall errors does Venus handle ? */
1336e51f8aaSJan Harkes error = coda_downcall(vcp, hdr.opcode, dcbuf, nbytes);
1341da177e4SLinus Torvalds
135936dae45SDan Carpenter kvfree(dcbuf);
1361da177e4SLinus Torvalds if (error) {
1376d6bd94fSFabian Frederick pr_warn("%s: coda_downcall error: %d\n",
1386d6bd94fSFabian Frederick __func__, error);
1391da177e4SLinus Torvalds retval = error;
1401da177e4SLinus Torvalds goto out;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds count = nbytes;
1431da177e4SLinus Torvalds goto out;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds /* Look for the message on the processing queue. */
147da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
1481da177e4SLinus Torvalds list_for_each(lh, &vcp->vc_processing) {
1491da177e4SLinus Torvalds tmp = list_entry(lh, struct upc_req , uc_chain);
1501da177e4SLinus Torvalds if (tmp->uc_unique == hdr.unique) {
1511da177e4SLinus Torvalds req = tmp;
1521da177e4SLinus Torvalds list_del(&req->uc_chain);
1531da177e4SLinus Torvalds break;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds }
156da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds if (!req) {
1596d6bd94fSFabian Frederick pr_warn("%s: msg (%d, %d) not found\n",
1606d6bd94fSFabian Frederick __func__, hdr.opcode, hdr.unique);
1611da177e4SLinus Torvalds retval = -ESRCH;
1621da177e4SLinus Torvalds goto out;
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds /* move data into response buffer. */
1661da177e4SLinus Torvalds if (req->uc_outSize < nbytes) {
1676d6bd94fSFabian Frederick pr_warn("%s: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
1686d6bd94fSFabian Frederick __func__, req->uc_outSize, (long)nbytes,
1696d6bd94fSFabian Frederick hdr.opcode, hdr.unique);
1701da177e4SLinus Torvalds nbytes = req->uc_outSize; /* don't have more space! */
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds if (copy_from_user(req->uc_data, buf, nbytes)) {
1734aeefdc6SJens Axboe req->uc_flags |= CODA_REQ_ABORT;
1741da177e4SLinus Torvalds wake_up(&req->uc_sleep);
1751da177e4SLinus Torvalds retval = -EFAULT;
1761da177e4SLinus Torvalds goto out;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds /* adjust outsize. is this useful ?? */
1801da177e4SLinus Torvalds req->uc_outSize = nbytes;
181112d421dSJan Harkes req->uc_flags |= CODA_REQ_WRITE;
1821da177e4SLinus Torvalds count = nbytes;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds /* Convert filedescriptor into a file handle */
1851da177e4SLinus Torvalds if (req->uc_opcode == CODA_OPEN_BY_FD) {
1861da177e4SLinus Torvalds struct coda_open_by_fd_out *outp =
1871da177e4SLinus Torvalds (struct coda_open_by_fd_out *)req->uc_data;
18802551c23SZhouyang Jia if (!outp->oh.result) {
1891da177e4SLinus Torvalds outp->fh = fget(outp->fd);
19002551c23SZhouyang Jia if (!outp->fh)
19102551c23SZhouyang Jia return -EBADF;
19202551c23SZhouyang Jia }
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds
1951da177e4SLinus Torvalds wake_up(&req->uc_sleep);
1961da177e4SLinus Torvalds out:
1971da177e4SLinus Torvalds return(count ? count : retval);
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds /*
2011da177e4SLinus Torvalds * Read a message from the kernel to Venus
2021da177e4SLinus Torvalds */
2031da177e4SLinus Torvalds
coda_psdev_read(struct file * file,char __user * buf,size_t nbytes,loff_t * off)2041da177e4SLinus Torvalds static ssize_t coda_psdev_read(struct file * file, char __user * buf,
2051da177e4SLinus Torvalds size_t nbytes, loff_t *off)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds DECLARE_WAITQUEUE(wait, current);
2081da177e4SLinus Torvalds struct venus_comm *vcp = (struct venus_comm *) file->private_data;
2091da177e4SLinus Torvalds struct upc_req *req;
2101da177e4SLinus Torvalds ssize_t retval = 0, count = 0;
2111da177e4SLinus Torvalds
2121da177e4SLinus Torvalds if (nbytes == 0)
2131da177e4SLinus Torvalds return 0;
2141da177e4SLinus Torvalds
215da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
2161da177e4SLinus Torvalds
2171da177e4SLinus Torvalds add_wait_queue(&vcp->vc_waitq, &wait);
2181da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE);
2191da177e4SLinus Torvalds
2201da177e4SLinus Torvalds while (list_empty(&vcp->vc_pending)) {
2211da177e4SLinus Torvalds if (file->f_flags & O_NONBLOCK) {
2221da177e4SLinus Torvalds retval = -EAGAIN;
2231da177e4SLinus Torvalds break;
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds if (signal_pending(current)) {
2261da177e4SLinus Torvalds retval = -ERESTARTSYS;
2271da177e4SLinus Torvalds break;
2281da177e4SLinus Torvalds }
229da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
2301da177e4SLinus Torvalds schedule();
231da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds set_current_state(TASK_RUNNING);
2351da177e4SLinus Torvalds remove_wait_queue(&vcp->vc_waitq, &wait);
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds if (retval)
2381da177e4SLinus Torvalds goto out;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
2411da177e4SLinus Torvalds list_del(&req->uc_chain);
2421da177e4SLinus Torvalds
2431da177e4SLinus Torvalds /* Move the input args into userspace */
2441da177e4SLinus Torvalds count = req->uc_inSize;
2451da177e4SLinus Torvalds if (nbytes < req->uc_inSize) {
2466d6bd94fSFabian Frederick pr_warn("%s: Venus read %ld bytes of %d in message\n",
2476d6bd94fSFabian Frederick __func__, (long)nbytes, req->uc_inSize);
2481da177e4SLinus Torvalds count = nbytes;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds if (copy_to_user(buf, req->uc_data, count))
2521da177e4SLinus Torvalds retval = -EFAULT;
2531da177e4SLinus Torvalds
2541da177e4SLinus Torvalds /* If request was not a signal, enqueue and don't free */
2554aeefdc6SJens Axboe if (!(req->uc_flags & CODA_REQ_ASYNC)) {
2564aeefdc6SJens Axboe req->uc_flags |= CODA_REQ_READ;
2578e13059aSAkinobu Mita list_add_tail(&(req->uc_chain), &vcp->vc_processing);
2581da177e4SLinus Torvalds goto out;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds
261936dae45SDan Carpenter kvfree(req->uc_data);
26237461e19SJan Harkes kfree(req);
2631da177e4SLinus Torvalds out:
264da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
2651da177e4SLinus Torvalds return (count ? count : retval);
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds
coda_psdev_open(struct inode * inode,struct file * file)2681da177e4SLinus Torvalds static int coda_psdev_open(struct inode * inode, struct file * file)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds struct venus_comm *vcp;
27187065519SJan Harkes int idx, err;
27287065519SJan Harkes
2739fd973e0SEric W. Biederman if (task_active_pid_ns(current) != &init_pid_ns)
2749fd973e0SEric W. Biederman return -EINVAL;
2759fd973e0SEric W. Biederman
276d83f5901SEric W. Biederman if (current_user_ns() != &init_user_ns)
277d83f5901SEric W. Biederman return -EINVAL;
278d83f5901SEric W. Biederman
27987065519SJan Harkes idx = iminor(inode);
28087065519SJan Harkes if (idx < 0 || idx >= MAX_CODADEVS)
28187065519SJan Harkes return -ENODEV;
2821da177e4SLinus Torvalds
28387065519SJan Harkes err = -EBUSY;
2841da177e4SLinus Torvalds vcp = &coda_comms[idx];
285da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
286da47c19eSYoshihisa Abe
28787065519SJan Harkes if (!vcp->vc_inuse) {
28887065519SJan Harkes vcp->vc_inuse++;
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds INIT_LIST_HEAD(&vcp->vc_pending);
2911da177e4SLinus Torvalds INIT_LIST_HEAD(&vcp->vc_processing);
2921da177e4SLinus Torvalds init_waitqueue_head(&vcp->vc_waitq);
2931da177e4SLinus Torvalds vcp->vc_sb = NULL;
2941da177e4SLinus Torvalds vcp->vc_seq = 0;
2951da177e4SLinus Torvalds
2961da177e4SLinus Torvalds file->private_data = vcp;
29787065519SJan Harkes err = 0;
29887065519SJan Harkes }
2991da177e4SLinus Torvalds
300da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
30187065519SJan Harkes return err;
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds
coda_psdev_release(struct inode * inode,struct file * file)3051da177e4SLinus Torvalds static int coda_psdev_release(struct inode * inode, struct file * file)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds struct venus_comm *vcp = (struct venus_comm *) file->private_data;
3081da177e4SLinus Torvalds struct upc_req *req, *tmp;
3091da177e4SLinus Torvalds
31087065519SJan Harkes if (!vcp || !vcp->vc_inuse ) {
3116d6bd94fSFabian Frederick pr_warn("%s: Not open.\n", __func__);
3121da177e4SLinus Torvalds return -1;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds
315da47c19eSYoshihisa Abe mutex_lock(&vcp->vc_mutex);
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds /* Wakeup clients so they can return. */
3181da177e4SLinus Torvalds list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
31987065519SJan Harkes list_del(&req->uc_chain);
32087065519SJan Harkes
3211da177e4SLinus Torvalds /* Async requests need to be freed here */
3224aeefdc6SJens Axboe if (req->uc_flags & CODA_REQ_ASYNC) {
323936dae45SDan Carpenter kvfree(req->uc_data);
32437461e19SJan Harkes kfree(req);
3251da177e4SLinus Torvalds continue;
3261da177e4SLinus Torvalds }
3274aeefdc6SJens Axboe req->uc_flags |= CODA_REQ_ABORT;
3281da177e4SLinus Torvalds wake_up(&req->uc_sleep);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds
33187065519SJan Harkes list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
33287065519SJan Harkes list_del(&req->uc_chain);
33387065519SJan Harkes
3344aeefdc6SJens Axboe req->uc_flags |= CODA_REQ_ABORT;
3351da177e4SLinus Torvalds wake_up(&req->uc_sleep);
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds
33887065519SJan Harkes file->private_data = NULL;
33987065519SJan Harkes vcp->vc_inuse--;
340da47c19eSYoshihisa Abe mutex_unlock(&vcp->vc_mutex);
3411da177e4SLinus Torvalds return 0;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds
3441da177e4SLinus Torvalds
3454b6f5d20SArjan van de Ven static const struct file_operations coda_psdev_fops = {
3461da177e4SLinus Torvalds .owner = THIS_MODULE,
3471da177e4SLinus Torvalds .read = coda_psdev_read,
3481da177e4SLinus Torvalds .write = coda_psdev_write,
3491da177e4SLinus Torvalds .poll = coda_psdev_poll,
35097718390SArnd Bergmann .unlocked_ioctl = coda_psdev_ioctl,
3511da177e4SLinus Torvalds .open = coda_psdev_open,
3521da177e4SLinus Torvalds .release = coda_psdev_release,
3536038f373SArnd Bergmann .llseek = noop_llseek,
3541da177e4SLinus Torvalds };
3551da177e4SLinus Torvalds
init_coda_psdev(void)356f9484528SFabian Frederick static int __init init_coda_psdev(void)
3571da177e4SLinus Torvalds {
3581da177e4SLinus Torvalds int i, err = 0;
3591da177e4SLinus Torvalds if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
3606d6bd94fSFabian Frederick pr_err("%s: unable to get major %d\n",
3616d6bd94fSFabian Frederick __func__, CODA_PSDEV_MAJOR);
3621da177e4SLinus Torvalds return -EIO;
3631da177e4SLinus Torvalds }
364*1aaba11dSGreg Kroah-Hartman coda_psdev_class = class_create("coda");
3651da177e4SLinus Torvalds if (IS_ERR(coda_psdev_class)) {
3661da177e4SLinus Torvalds err = PTR_ERR(coda_psdev_class);
3671da177e4SLinus Torvalds goto out_chrdev;
3681da177e4SLinus Torvalds }
369da47c19eSYoshihisa Abe for (i = 0; i < MAX_CODADEVS; i++) {
370da47c19eSYoshihisa Abe mutex_init(&(&coda_comms[i])->vc_mutex);
371a9b12619SGreg Kroah-Hartman device_create(coda_psdev_class, NULL,
372a9b12619SGreg Kroah-Hartman MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i);
373da47c19eSYoshihisa Abe }
3741da177e4SLinus Torvalds coda_sysctl_init();
3751da177e4SLinus Torvalds goto out;
3761da177e4SLinus Torvalds
3771da177e4SLinus Torvalds out_chrdev:
3781da177e4SLinus Torvalds unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
3791da177e4SLinus Torvalds out:
3801da177e4SLinus Torvalds return err;
3811da177e4SLinus Torvalds }
3821da177e4SLinus Torvalds
3835b7f13bdSJan Harkes MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
3845b7f13bdSJan Harkes MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
3855b7f13bdSJan Harkes MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
3861da177e4SLinus Torvalds MODULE_LICENSE("GPL");
38798d5b61eSJan Harkes MODULE_VERSION("7.2");
3881da177e4SLinus Torvalds
init_coda(void)3891da177e4SLinus Torvalds static int __init init_coda(void)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds int status;
3921da177e4SLinus Torvalds int i;
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds status = coda_init_inodecache();
3951da177e4SLinus Torvalds if (status)
3961da177e4SLinus Torvalds goto out2;
3971da177e4SLinus Torvalds status = init_coda_psdev();
3981da177e4SLinus Torvalds if ( status ) {
399d9b4b319SFabian Frederick pr_warn("Problem (%d) in init_coda_psdev\n", status);
4001da177e4SLinus Torvalds goto out1;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds
4031da177e4SLinus Torvalds status = register_filesystem(&coda_fs_type);
4041da177e4SLinus Torvalds if (status) {
405f38cfb25SFabian Frederick pr_warn("failed to register filesystem!\n");
4061da177e4SLinus Torvalds goto out;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds return 0;
4091da177e4SLinus Torvalds out:
4108ab5e4c1SGreg Kroah-Hartman for (i = 0; i < MAX_CODADEVS; i++)
41162ca8792SKay Sievers device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
4121db560afSgregkh@suse.de class_destroy(coda_psdev_class);
4131da177e4SLinus Torvalds unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
4141da177e4SLinus Torvalds coda_sysctl_clean();
4151da177e4SLinus Torvalds out1:
4161da177e4SLinus Torvalds coda_destroy_inodecache();
4171da177e4SLinus Torvalds out2:
4181da177e4SLinus Torvalds return status;
4191da177e4SLinus Torvalds }
4201da177e4SLinus Torvalds
exit_coda(void)4211da177e4SLinus Torvalds static void __exit exit_coda(void)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds int err, i;
4241da177e4SLinus Torvalds
4251da177e4SLinus Torvalds err = unregister_filesystem(&coda_fs_type);
426d9b4b319SFabian Frederick if (err != 0)
427f38cfb25SFabian Frederick pr_warn("failed to unregister filesystem\n");
4288ab5e4c1SGreg Kroah-Hartman for (i = 0; i < MAX_CODADEVS; i++)
42962ca8792SKay Sievers device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
4301db560afSgregkh@suse.de class_destroy(coda_psdev_class);
4311da177e4SLinus Torvalds unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
4321da177e4SLinus Torvalds coda_sysctl_clean();
4331da177e4SLinus Torvalds coda_destroy_inodecache();
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds module_init(init_coda);
4371da177e4SLinus Torvalds module_exit(exit_coda);
4381da177e4SLinus Torvalds
439