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