2 * An implementation of a loadable kernel mode driver providing
3 * multiple kernel/user space bidirectional communications links.
5 * Author: Alan Cox <alan@redhat.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
12 * Adapted to become the Linux 2.0 Coda pseudo device
13 * Peter Braam <braam@maths.ox.ac.uk>
14 * Michael Callahan <mjc@emmy.smith.edu>
16 * Changes for Linux 2.1
17 * Copyright (c) 1997 Carnegie-Mellon University
20 #include <linux/module.h>
21 #include <linux/errno.h>
22 #include <linux/kernel.h>
23 #include <linux/major.h>
24 #include <linux/sched.h>
26 #include <linux/malloc.h>
27 #include <linux/ioport.h>
28 #include <linux/fcntl.h>
29 #include <linux/delay.h>
30 #include <linux/skbuff.h>
31 #include <linux/proc_fs.h>
32 #include <linux/devfs_fs_kernel.h>
33 #include <linux/vmalloc.h>
35 #include <linux/poll.h>
36 #include <linux/init.h>
37 #include <linux/list.h>
38 #include <linux/smp_lock.h>
40 #include <asm/segment.h>
41 #include <asm/system.h>
43 #include <asm/uaccess.h>
45 #include <linux/coda.h>
46 #include <linux/coda_linux.h>
47 #include <linux/coda_fs_i.h>
48 #include <linux/coda_psdev.h>
49 #include <linux/coda_cache.h>
50 #include <linux/coda_proc.h>
55 extern struct file_system_type coda_fs_type
;
56 extern int init_coda_fs(void);
59 int coda_hard
= 0; /* allows signals during upcalls */
60 unsigned long coda_timeout
= 30; /* .. secs, then signals will dequeue */
62 struct coda_sb_info coda_super_info
;
63 struct venus_comm coda_upc_comm
;
69 static unsigned int coda_psdev_poll(struct file
*file
, poll_table
* wait
)
71 struct venus_comm
*vcp
= &coda_upc_comm
;
72 unsigned int mask
= POLLOUT
| POLLWRNORM
;
74 poll_wait(file
, &vcp
->vc_waitq
, wait
);
75 if (!list_empty(&vcp
->vc_pending
))
76 mask
|= POLLIN
| POLLRDNORM
;
81 static int coda_psdev_ioctl(struct inode
* inode
, struct file
* filp
,
82 unsigned int cmd
, unsigned long arg
)
87 case CIOC_KERNEL_VERSION
:
88 data
= CODA_KERNEL_VERSION
;
89 return put_user(data
, (int *) arg
);
98 * Receive a message written by Venus to the psdev
101 static ssize_t
coda_psdev_write(struct file
*file
, const char *buf
,
102 size_t nbytes
, loff_t
*off
)
104 struct venus_comm
*vcp
= &coda_upc_comm
;
105 struct upc_req
*req
= NULL
;
107 struct list_head
*lh
;
108 struct coda_in_hdr hdr
;
109 ssize_t retval
= 0, count
= 0;
112 if ( !coda_upc_comm
.vc_pid
)
114 /* Peek at the opcode, uniquefier */
115 if (copy_from_user(&hdr
, buf
, 2 * sizeof(u_long
)))
118 CDEBUG(D_PSDEV
, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ld\n",
119 current
->pid
, hdr
.opcode
, hdr
.unique
, (long)nbytes
);
121 if (DOWNCALL(hdr
.opcode
)) {
122 struct super_block
*sb
= NULL
;
123 union outputArgs
*dcbuf
;
124 int size
= sizeof(*dcbuf
);
126 sb
= coda_super_info
.sbi_sb
;
128 CDEBUG(D_PSDEV
, "coda_psdev_write: downcall, no SB!\n");
132 CDEBUG(D_PSDEV
, "handling downcall\n");
134 if ( nbytes
< sizeof(struct coda_out_hdr
) ) {
135 printk("coda_downcall opc %ld uniq %ld, not enough!\n",
136 hdr
.opcode
, hdr
.unique
);
140 if ( nbytes
> size
) {
141 printk("Coda: downcall opc %ld, uniq %ld, too much!",
142 hdr
.opcode
, hdr
.unique
);
145 CODA_ALLOC(dcbuf
, union outputArgs
*, nbytes
);
146 if (copy_from_user(dcbuf
, buf
, nbytes
)) {
147 CODA_FREE(dcbuf
, nbytes
);
152 /* what downcall errors does Venus handle ? */
154 error
= coda_downcall(hdr
.opcode
, dcbuf
, sb
);
157 CODA_FREE(dcbuf
, nbytes
);
159 printk("psdev_write: coda_downcall error: %d\n", error
);
167 /* Look for the message on the processing queue. */
169 lh
= &vcp
->vc_processing
;
170 while ( (lh
= lh
->next
) != &vcp
->vc_processing
) {
171 tmp
= list_entry(lh
, struct upc_req
, uc_chain
);
172 if (tmp
->uc_unique
== hdr
.unique
) {
174 list_del(&req
->uc_chain
);
175 CDEBUG(D_PSDEV
,"Eureka: uniq %ld on queue!\n",
183 printk("psdev_write: msg (%ld, %ld) not found\n",
184 hdr
.opcode
, hdr
.unique
);
189 /* move data into response buffer. */
190 if (req
->uc_outSize
< nbytes
) {
191 printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n",
192 req
->uc_outSize
, (long)nbytes
, hdr
.opcode
, hdr
.unique
);
193 nbytes
= req
->uc_outSize
; /* don't have more space! */
195 if (copy_from_user(req
->uc_data
, buf
, nbytes
)) {
196 req
->uc_flags
|= REQ_ABORT
;
197 wake_up(&req
->uc_sleep
);
202 /* adjust outsize. is this usefull ?? */
203 req
->uc_outSize
= nbytes
;
204 req
->uc_flags
|= REQ_WRITE
;
208 "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n",
209 (long)count
, hdr
.opcode
, hdr
.unique
, &req
);
211 wake_up(&req
->uc_sleep
);
213 return(count
? count
: retval
);
217 * Read a message from the kernel to Venus
220 static ssize_t
coda_psdev_read(struct file
* file
, char * buf
,
221 size_t nbytes
, loff_t
*off
)
223 DECLARE_WAITQUEUE(wait
, current
);
224 struct venus_comm
*vcp
= &coda_upc_comm
;
226 ssize_t retval
= 0, count
= 0;
233 add_wait_queue(&vcp
->vc_waitq
, &wait
);
234 set_current_state(TASK_INTERRUPTIBLE
);
236 while (list_empty(&vcp
->vc_pending
)) {
237 if (file
->f_flags
& O_NONBLOCK
) {
241 if (signal_pending(current
)) {
242 retval
= -ERESTARTSYS
;
248 current
->state
= TASK_RUNNING
;
249 remove_wait_queue(&vcp
->vc_waitq
, &wait
);
254 req
= list_entry(vcp
->vc_pending
.next
, struct upc_req
,uc_chain
);
255 list_del(&req
->uc_chain
);
257 /* Move the input args into userspace */
258 count
= req
->uc_inSize
;
259 if (nbytes
< req
->uc_inSize
) {
260 printk ("psdev_read: Venus read %ld bytes of %d in message\n",
261 (long)nbytes
, req
->uc_inSize
);
265 if (copy_to_user(buf
, req
->uc_data
, count
)) {
270 /* If request was not a signal, enqueue and don't free */
271 if (req
->uc_opcode
!= CODA_SIGNAL
) {
272 req
->uc_flags
|= REQ_READ
;
273 list_add(&(req
->uc_chain
), vcp
->vc_processing
.prev
);
277 CDEBUG(D_PSDEV
, "vcread: signal msg (%d, %d)\n",
278 req
->uc_opcode
, req
->uc_unique
);
281 CODA_FREE(req
->uc_data
, sizeof(struct coda_in_hdr
));
282 CODA_FREE(req
, sizeof(struct upc_req
));
285 return (count
? count
: retval
);
289 static int coda_psdev_open(struct inode
* inode
, struct file
* file
)
291 struct venus_comm
*vcp
= &coda_upc_comm
;
294 /* first opener: must be lento. Initialize & take its pid */
295 if ( (file
->f_flags
& O_ACCMODE
) == O_RDWR
) {
297 printk("Venus pid already set to %d!!\n", vcp
->vc_pid
);
300 if ( vcp
->vc_inuse
) {
301 printk("psdev_open: Cannot O_RDWR while open.\n");
308 if ( (file
->f_flags
& O_ACCMODE
) == O_RDWR
) {
309 vcp
->vc_pid
= current
->pid
;
311 INIT_LIST_HEAD(&vcp
->vc_pending
);
312 INIT_LIST_HEAD(&vcp
->vc_processing
);
315 CDEBUG(D_PSDEV
, "inuse: %d, vc_pid %d, caller %d\n",
316 vcp
->vc_inuse
, vcp
->vc_pid
, current
->pid
);
323 static int coda_psdev_release(struct inode
* inode
, struct file
* file
)
325 struct venus_comm
*vcp
= &coda_upc_comm
;
327 struct list_head
*lh
, *next
;
330 if ( !vcp
->vc_inuse
) {
331 printk("psdev_release: Not open.\n");
336 CDEBUG(D_PSDEV
, "inuse: %d, vc_pid %d, caller %d\n",
337 vcp
->vc_inuse
, vcp
->vc_pid
, current
->pid
);
339 if ( vcp
->vc_pid
!= current
->pid
) {
340 /* FIXME: this is broken. If venus does fork(), accounting goes wrong */
341 printk( "Closed by someone else than caller?\n" );
346 /* Wakeup clients so they can return. */
347 CDEBUG(D_PSDEV
, "wake up pending clients\n");
348 lh
= vcp
->vc_pending
.next
;
350 while ( (lh
= next
) != &vcp
->vc_pending
) {
352 req
= list_entry(lh
, struct upc_req
, uc_chain
);
353 /* Async requests need to be freed here */
354 if (req
->uc_flags
& REQ_ASYNC
) {
355 CODA_FREE(req
->uc_data
, sizeof(struct coda_in_hdr
));
356 CODA_FREE(req
, (u_int
)sizeof(struct upc_req
));
359 req
->uc_flags
|= REQ_ABORT
;
360 wake_up(&req
->uc_sleep
);
363 lh
= &vcp
->vc_processing
;
364 CDEBUG(D_PSDEV
, "wake up processing clients\n");
365 while ( (lh
= lh
->next
) != &vcp
->vc_processing
) {
366 req
= list_entry(lh
, struct upc_req
, uc_chain
);
367 req
->uc_flags
|= REQ_ABORT
;
368 wake_up(&req
->uc_sleep
);
370 CDEBUG(D_PSDEV
, "Done.\n");
377 static struct file_operations coda_psdev_fops
= {
379 read
: coda_psdev_read
,
380 write
: coda_psdev_write
,
381 poll
: coda_psdev_poll
,
382 ioctl
: coda_psdev_ioctl
,
383 open
: coda_psdev_open
,
384 release
: coda_psdev_release
,
389 int __init
init_coda(void)
392 printk(KERN_INFO
"Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n");
394 status
= init_coda_psdev();
396 printk("Problem (%d) in init_coda_psdev\n", status
);
400 status
= init_coda_fs();
402 printk("coda: failed in init_coda_fs!\n");
407 static devfs_handle_t devfs_handle
= NULL
;
409 int init_coda_psdev(void)
411 if(devfs_register_chrdev(CODA_PSDEV_MAJOR
,"coda_psdev",
413 printk(KERN_ERR
"coda_psdev: unable to get major %d\n",
417 devfs_handle
= devfs_mk_dir (NULL
, "coda", 4, NULL
);
418 devfs_register_series (devfs_handle
, "%u", MAX_CODADEVS
, DEVFS_FL_NONE
,
420 S_IFCHR
| S_IRUSR
| S_IWUSR
, 0, 0,
421 &coda_psdev_fops
, NULL
);
422 memset(&coda_upc_comm
, 0, sizeof(coda_upc_comm
));
423 memset(&coda_super_info
, 0, sizeof(coda_super_info
));
424 init_waitqueue_head(&coda_upc_comm
.vc_waitq
);
434 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
436 int init_module(void)
439 printk(KERN_INFO
"Coda Kernel/Venus communications (module), v5.0-pre1, braam@cs.cmu.edu.\n");
441 status
= init_coda_psdev();
443 printk("Problem (%d) in init_coda_psdev\n", status
);
447 status
= init_coda_fs();
449 printk("coda: failed in init_coda_fs!\n");
455 void cleanup_module(void)
461 if ( (err
= unregister_filesystem(&coda_fs_type
)) != 0 ) {
462 printk("coda: failed to unregister filesystem\n");
464 devfs_unregister (devfs_handle
);
465 devfs_unregister_chrdev(CODA_PSDEV_MAJOR
,"coda_psdev");