xref: /openbmc/linux/fs/coda/psdev.c (revision 2427f03f)
1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /*
3   *      	An implementation of a loadable kernel mode driver providing
4   *		multiple kernel/user space bidirectional communications links.
5   *
6   * 		Author: 	Alan Cox <alan@lxorguk.ukuu.org.uk>
7   *
8   *              Adapted to become the Linux 2.0 Coda pseudo device
9   *              Peter  Braam  <braam@maths.ox.ac.uk>
10   *              Michael Callahan <mjc@emmy.smith.edu>
11   *
12   *              Changes for Linux 2.1
13   *              Copyright (c) 1997 Carnegie-Mellon University
14   */
15  
16  #include <linux/module.h>
17  #include <linux/errno.h>
18  #include <linux/kernel.h>
19  #include <linux/major.h>
20  #include <linux/time.h>
21  #include <linux/sched/signal.h>
22  #include <linux/slab.h>
23  #include <linux/ioport.h>
24  #include <linux/fcntl.h>
25  #include <linux/delay.h>
26  #include <linux/skbuff.h>
27  #include <linux/proc_fs.h>
28  #include <linux/vmalloc.h>
29  #include <linux/fs.h>
30  #include <linux/file.h>
31  #include <linux/poll.h>
32  #include <linux/init.h>
33  #include <linux/list.h>
34  #include <linux/mutex.h>
35  #include <linux/device.h>
36  #include <linux/pid_namespace.h>
37  #include <asm/io.h>
38  #include <linux/uaccess.h>
39  
40  #include <linux/coda.h>
41  #include "coda_psdev.h"
42  #include "coda_linux.h"
43  
44  #include "coda_int.h"
45  
46  /* statistics */
47  int           coda_hard;         /* allows signals during upcalls */
48  unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
49  
50  
51  struct venus_comm coda_comms[MAX_CODADEVS];
52  static struct class *coda_psdev_class;
53  
54  /*
55   * Device operations
56   */
57  
58  static __poll_t coda_psdev_poll(struct file *file, poll_table * wait)
59  {
60          struct venus_comm *vcp = (struct venus_comm *) file->private_data;
61  	__poll_t mask = EPOLLOUT | EPOLLWRNORM;
62  
63  	poll_wait(file, &vcp->vc_waitq, wait);
64  	mutex_lock(&vcp->vc_mutex);
65  	if (!list_empty(&vcp->vc_pending))
66                  mask |= EPOLLIN | EPOLLRDNORM;
67  	mutex_unlock(&vcp->vc_mutex);
68  
69  	return mask;
70  }
71  
72  static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
73  {
74  	unsigned int data;
75  
76  	switch(cmd) {
77  	case CIOC_KERNEL_VERSION:
78  		data = CODA_KERNEL_VERSION;
79  		return put_user(data, (int __user *) arg);
80  	default:
81  		return -ENOTTY;
82  	}
83  
84  	return 0;
85  }
86  
87  /*
88   *	Receive a message written by Venus to the psdev
89   */
90  
91  static ssize_t coda_psdev_write(struct file *file, const char __user *buf,
92  				size_t nbytes, loff_t *off)
93  {
94          struct venus_comm *vcp = (struct venus_comm *) file->private_data;
95          struct upc_req *req = NULL;
96          struct upc_req *tmp;
97  	struct list_head *lh;
98  	struct coda_in_hdr hdr;
99  	ssize_t retval = 0, count = 0;
100  	int error;
101  
102  	/* make sure there is enough to copy out the (opcode, unique) values */
103  	if (nbytes < (2 * sizeof(u_int32_t)))
104  		return -EINVAL;
105  
106          /* Peek at the opcode, uniquefier */
107  	if (copy_from_user(&hdr, buf, 2 * sizeof(u_int32_t)))
108  	        return -EFAULT;
109  
110          if (DOWNCALL(hdr.opcode)) {
111  		union outputArgs *dcbuf;
112  		int size = sizeof(*dcbuf);
113  
114  		if  ( nbytes < sizeof(struct coda_out_hdr) ) {
115  			pr_warn("coda_downcall opc %d uniq %d, not enough!\n",
116  				hdr.opcode, hdr.unique);
117  			count = nbytes;
118  			goto out;
119  		}
120  		if ( nbytes > size ) {
121  			pr_warn("downcall opc %d, uniq %d, too much!",
122  				hdr.opcode, hdr.unique);
123  		        nbytes = size;
124  		}
125  
126  		dcbuf = vmemdup_user(buf, nbytes);
127  		if (IS_ERR(dcbuf)) {
128  			retval = PTR_ERR(dcbuf);
129  			goto out;
130  		}
131  
132  		/* what downcall errors does Venus handle ? */
133  		error = coda_downcall(vcp, hdr.opcode, dcbuf, nbytes);
134  
135  		kvfree(dcbuf);
136  		if (error) {
137  			pr_warn("%s: coda_downcall error: %d\n",
138  				__func__, 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  		pr_warn("%s: msg (%d, %d) not found\n",
160  			__func__, 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  		pr_warn("%s: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
168  			__func__, req->uc_outSize, (long)nbytes,
169  			hdr.opcode, hdr.unique);
170  		nbytes = req->uc_outSize; /* don't have more space! */
171  	}
172          if (copy_from_user(req->uc_data, buf, nbytes)) {
173  		req->uc_flags |= CODA_REQ_ABORT;
174  		wake_up(&req->uc_sleep);
175  		retval = -EFAULT;
176  		goto out;
177  	}
178  
179  	/* adjust outsize. is this useful ?? */
180  	req->uc_outSize = nbytes;
181  	req->uc_flags |= CODA_REQ_WRITE;
182  	count = nbytes;
183  
184  	/* Convert filedescriptor into a file handle */
185  	if (req->uc_opcode == CODA_OPEN_BY_FD) {
186  		struct coda_open_by_fd_out *outp =
187  			(struct coda_open_by_fd_out *)req->uc_data;
188  		if (!outp->oh.result) {
189  			outp->fh = fget(outp->fd);
190  			if (!outp->fh)
191  				return -EBADF;
192  		}
193  	}
194  
195          wake_up(&req->uc_sleep);
196  out:
197          return(count ? count : retval);
198  }
199  
200  /*
201   *	Read a message from the kernel to Venus
202   */
203  
204  static ssize_t coda_psdev_read(struct file * file, char __user * buf,
205  			       size_t nbytes, loff_t *off)
206  {
207  	DECLARE_WAITQUEUE(wait, current);
208          struct venus_comm *vcp = (struct venus_comm *) file->private_data;
209          struct upc_req *req;
210  	ssize_t retval = 0, count = 0;
211  
212  	if (nbytes == 0)
213  		return 0;
214  
215  	mutex_lock(&vcp->vc_mutex);
216  
217  	add_wait_queue(&vcp->vc_waitq, &wait);
218  	set_current_state(TASK_INTERRUPTIBLE);
219  
220  	while (list_empty(&vcp->vc_pending)) {
221  		if (file->f_flags & O_NONBLOCK) {
222  			retval = -EAGAIN;
223  			break;
224  		}
225  		if (signal_pending(current)) {
226  			retval = -ERESTARTSYS;
227  			break;
228  		}
229  		mutex_unlock(&vcp->vc_mutex);
230  		schedule();
231  		mutex_lock(&vcp->vc_mutex);
232  	}
233  
234  	set_current_state(TASK_RUNNING);
235  	remove_wait_queue(&vcp->vc_waitq, &wait);
236  
237  	if (retval)
238  		goto out;
239  
240  	req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
241  	list_del(&req->uc_chain);
242  
243  	/* Move the input args into userspace */
244  	count = req->uc_inSize;
245  	if (nbytes < req->uc_inSize) {
246  		pr_warn("%s: Venus read %ld bytes of %d in message\n",
247  			__func__, (long)nbytes, req->uc_inSize);
248  		count = nbytes;
249          }
250  
251  	if (copy_to_user(buf, req->uc_data, count))
252  	        retval = -EFAULT;
253  
254  	/* If request was not a signal, enqueue and don't free */
255  	if (!(req->uc_flags & CODA_REQ_ASYNC)) {
256  		req->uc_flags |= CODA_REQ_READ;
257  		list_add_tail(&(req->uc_chain), &vcp->vc_processing);
258  		goto out;
259  	}
260  
261  	kvfree(req->uc_data);
262  	kfree(req);
263  out:
264  	mutex_unlock(&vcp->vc_mutex);
265  	return (count ? count : retval);
266  }
267  
268  static int coda_psdev_open(struct inode * inode, struct file * file)
269  {
270  	struct venus_comm *vcp;
271  	int idx, err;
272  
273  	if (task_active_pid_ns(current) != &init_pid_ns)
274  		return -EINVAL;
275  
276  	if (current_user_ns() != &init_user_ns)
277  		return -EINVAL;
278  
279  	idx = iminor(inode);
280  	if (idx < 0 || idx >= MAX_CODADEVS)
281  		return -ENODEV;
282  
283  	err = -EBUSY;
284  	vcp = &coda_comms[idx];
285  	mutex_lock(&vcp->vc_mutex);
286  
287  	if (!vcp->vc_inuse) {
288  		vcp->vc_inuse++;
289  
290  		INIT_LIST_HEAD(&vcp->vc_pending);
291  		INIT_LIST_HEAD(&vcp->vc_processing);
292  		init_waitqueue_head(&vcp->vc_waitq);
293  		vcp->vc_sb = NULL;
294  		vcp->vc_seq = 0;
295  
296  		file->private_data = vcp;
297  		err = 0;
298  	}
299  
300  	mutex_unlock(&vcp->vc_mutex);
301  	return err;
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  	if (!vcp || !vcp->vc_inuse ) {
311  		pr_warn("%s: Not open.\n", __func__);
312  		return -1;
313  	}
314  
315  	mutex_lock(&vcp->vc_mutex);
316  
317  	/* Wakeup clients so they can return. */
318  	list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
319  		list_del(&req->uc_chain);
320  
321  		/* Async requests need to be freed here */
322  		if (req->uc_flags & CODA_REQ_ASYNC) {
323  			kvfree(req->uc_data);
324  			kfree(req);
325  			continue;
326  		}
327  		req->uc_flags |= CODA_REQ_ABORT;
328  		wake_up(&req->uc_sleep);
329  	}
330  
331  	list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
332  		list_del(&req->uc_chain);
333  
334  		req->uc_flags |= CODA_REQ_ABORT;
335  		wake_up(&req->uc_sleep);
336  	}
337  
338  	file->private_data = NULL;
339  	vcp->vc_inuse--;
340  	mutex_unlock(&vcp->vc_mutex);
341  	return 0;
342  }
343  
344  
345  static const struct file_operations coda_psdev_fops = {
346  	.owner		= THIS_MODULE,
347  	.read		= coda_psdev_read,
348  	.write		= coda_psdev_write,
349  	.poll		= coda_psdev_poll,
350  	.unlocked_ioctl	= coda_psdev_ioctl,
351  	.open		= coda_psdev_open,
352  	.release	= coda_psdev_release,
353  	.llseek		= noop_llseek,
354  };
355  
356  static int __init init_coda_psdev(void)
357  {
358  	int i, err = 0;
359  	if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
360  		pr_err("%s: unable to get major %d\n",
361  		       __func__, CODA_PSDEV_MAJOR);
362  		return -EIO;
363  	}
364  	coda_psdev_class = class_create(THIS_MODULE, "coda");
365  	if (IS_ERR(coda_psdev_class)) {
366  		err = PTR_ERR(coda_psdev_class);
367  		goto out_chrdev;
368  	}
369  	for (i = 0; i < MAX_CODADEVS; i++) {
370  		mutex_init(&(&coda_comms[i])->vc_mutex);
371  		device_create(coda_psdev_class, NULL,
372  			      MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i);
373  	}
374  	coda_sysctl_init();
375  	goto out;
376  
377  out_chrdev:
378  	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
379  out:
380  	return err;
381  }
382  
383  MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
384  MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
385  MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
386  MODULE_LICENSE("GPL");
387  MODULE_VERSION("7.2");
388  
389  static int __init init_coda(void)
390  {
391  	int status;
392  	int i;
393  
394  	status = coda_init_inodecache();
395  	if (status)
396  		goto out2;
397  	status = init_coda_psdev();
398  	if ( status ) {
399  		pr_warn("Problem (%d) in init_coda_psdev\n", status);
400  		goto out1;
401  	}
402  
403  	status = register_filesystem(&coda_fs_type);
404  	if (status) {
405  		pr_warn("failed to register filesystem!\n");
406  		goto out;
407  	}
408  	return 0;
409  out:
410  	for (i = 0; i < MAX_CODADEVS; i++)
411  		device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
412  	class_destroy(coda_psdev_class);
413  	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
414  	coda_sysctl_clean();
415  out1:
416  	coda_destroy_inodecache();
417  out2:
418  	return status;
419  }
420  
421  static void __exit exit_coda(void)
422  {
423          int err, i;
424  
425  	err = unregister_filesystem(&coda_fs_type);
426  	if (err != 0)
427  		pr_warn("failed to unregister filesystem\n");
428  	for (i = 0; i < MAX_CODADEVS; i++)
429  		device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
430  	class_destroy(coda_psdev_class);
431  	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
432  	coda_sysctl_clean();
433  	coda_destroy_inodecache();
434  }
435  
436  module_init(init_coda);
437  module_exit(exit_coda);
438  
439