~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/coda/psdev.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *              An implementation of a loadable kernel mode driver providing
  3  *              multiple kernel/user space bidirectional communications links.
  4  *
  5  *              Author:         Alan Cox <alan@redhat.com>
  6  *
  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.
 11  * 
 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>           
 15  *
 16  *              Changes for Linux 2.1
 17  *              Copyright (c) 1997 Carnegie-Mellon University
 18  */
 19 
 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>
 25 #include <linux/lp.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>
 34 #include <linux/fs.h>
 35 #include <linux/poll.h>
 36 #include <linux/init.h>
 37 #include <linux/list.h>
 38 #include <linux/smp_lock.h>
 39 #include <asm/io.h>
 40 #include <asm/segment.h>
 41 #include <asm/system.h>
 42 #include <asm/poll.h>
 43 #include <asm/uaccess.h>
 44 
 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_proc.h>
 50 
 51 /* 
 52  * Coda stuff
 53  */
 54 extern struct file_system_type coda_fs_type;
 55 
 56 /* statistics */
 57 int           coda_hard;         /* allows signals during upcalls */
 58 unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
 59 
 60 
 61 struct venus_comm coda_comms[MAX_CODADEVS];
 62 
 63 /*
 64  * Device operations
 65  */
 66 
 67 static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
 68 {
 69         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
 70         unsigned int mask = POLLOUT | POLLWRNORM;
 71 
 72         poll_wait(file, &vcp->vc_waitq, wait);
 73         if (!list_empty(&vcp->vc_pending))
 74                 mask |= POLLIN | POLLRDNORM;
 75 
 76         return mask;
 77 }
 78 
 79 static int coda_psdev_ioctl(struct inode * inode, struct file * filp, 
 80                             unsigned int cmd, unsigned long arg)
 81 {
 82         unsigned int data;
 83 
 84         switch(cmd) {
 85         case CIOC_KERNEL_VERSION:
 86                 data = CODA_KERNEL_VERSION;
 87                 return put_user(data, (int *) arg);
 88         default:
 89                 return -ENOTTY;
 90         }
 91 
 92         return 0;
 93 }
 94 
 95 /*
 96  *      Receive a message written by Venus to the psdev
 97  */
 98  
 99 static ssize_t coda_psdev_write(struct file *file, const char *buf, 
100                                 size_t nbytes, loff_t *off)
101 {
102         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
103         struct upc_req *req = NULL;
104         struct upc_req *tmp;
105         struct list_head *lh;
106         struct coda_in_hdr hdr;
107         ssize_t retval = 0, count = 0;
108         int error;
109 
110         /* Peek at the opcode, uniquefier */
111         if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
112                 return -EFAULT;
113 
114         CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ld\n", 
115                current->pid, hdr.opcode, hdr.unique, (long)nbytes);
116 
117         if (DOWNCALL(hdr.opcode)) {
118                 struct super_block *sb = NULL;
119                 union outputArgs *dcbuf;
120                 int size = sizeof(*dcbuf);
121 
122                 sb = vcp->vc_sb;
123                 if ( !sb ) {
124                         CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n");
125                         count = nbytes;
126                         goto out;
127                 }
128                 CDEBUG(D_PSDEV, "handling downcall\n");
129 
130                 if  ( nbytes < sizeof(struct coda_out_hdr) ) {
131                         printk("coda_downcall opc %ld uniq %ld, not enough!\n",
132                                hdr.opcode, hdr.unique);
133                         count = nbytes;
134                         goto out;
135                 }
136                 if ( nbytes > size ) {
137                         printk("Coda: downcall opc %ld, uniq %ld, too much!",
138                                hdr.opcode, hdr.unique);
139                         nbytes = size;
140                 }
141                 CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
142                 if (copy_from_user(dcbuf, buf, nbytes)) {
143                         CODA_FREE(dcbuf, nbytes);
144                         retval = -EFAULT;
145                         goto out;
146                 }
147 
148                 /* what downcall errors does Venus handle ? */
149                 lock_kernel();
150                 error = coda_downcall(hdr.opcode, dcbuf, sb);
151                 unlock_kernel();
152 
153                 CODA_FREE(dcbuf, nbytes);
154                 if (error) {
155                         printk("psdev_write: coda_downcall error: %d\n", error);
156                         retval = error;
157                         goto out;
158                 }
159                 count = nbytes;
160                 goto out;
161         }
162         
163         /* Look for the message on the processing queue. */
164         lock_kernel();
165         lh  = &vcp->vc_processing;
166         while ( (lh = lh->next) != &vcp->vc_processing ) {
167                 tmp = list_entry(lh, struct upc_req , uc_chain);
168                 if (tmp->uc_unique == hdr.unique) {
169                         req = tmp;
170                         list_del(&req->uc_chain);
171                         CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", 
172                                hdr.unique);
173                         break;
174                 }
175         }
176         unlock_kernel();
177 
178         if (!req) {
179                 printk("psdev_write: msg (%ld, %ld) not found\n", 
180                        hdr.opcode, hdr.unique);
181                 retval = -ESRCH;
182                 goto out;
183         }
184 
185         /* move data into response buffer. */
186         if (req->uc_outSize < nbytes) {
187                 printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n",
188                        req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique);
189                 nbytes = req->uc_outSize; /* don't have more space! */
190         }
191         if (copy_from_user(req->uc_data, buf, nbytes)) {
192                 req->uc_flags |= REQ_ABORT;
193                 wake_up(&req->uc_sleep);
194                 retval = -EFAULT;
195                 goto out;
196         }
197 
198         /* adjust outsize. is this usefull ?? */
199         req->uc_outSize = nbytes;       
200         req->uc_flags |= REQ_WRITE;
201         count = nbytes;
202 
203         CDEBUG(D_PSDEV, 
204                "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n", 
205                 (long)count, hdr.opcode, hdr.unique, &req);
206 
207         wake_up(&req->uc_sleep);
208 out:
209         return(count ? count : retval);  
210 }
211 
212 /*
213  *      Read a message from the kernel to Venus
214  */
215 
216 static ssize_t coda_psdev_read(struct file * file, char * buf, 
217                                size_t nbytes, loff_t *off)
218 {
219         DECLARE_WAITQUEUE(wait, current);
220         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
221         struct upc_req *req;
222         ssize_t retval = 0, count = 0;
223 
224         if (nbytes == 0)
225                 return 0;
226 
227         lock_kernel();
228 
229         add_wait_queue(&vcp->vc_waitq, &wait);
230         set_current_state(TASK_INTERRUPTIBLE);
231 
232         while (list_empty(&vcp->vc_pending)) {
233                 if (file->f_flags & O_NONBLOCK) {
234                         retval = -EAGAIN;
235                         break;
236                 }
237                 if (signal_pending(current)) {
238                         retval = -ERESTARTSYS;
239                         break;
240                 }
241                 schedule();
242         }
243 
244         set_current_state(TASK_RUNNING);
245         remove_wait_queue(&vcp->vc_waitq, &wait);
246 
247         if (retval)
248                 goto out;
249 
250         req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
251         list_del(&req->uc_chain);
252 
253         /* Move the input args into userspace */
254         count = req->uc_inSize;
255         if (nbytes < req->uc_inSize) {
256                 printk ("psdev_read: Venus read %ld bytes of %d in message\n",
257                         (long)nbytes, req->uc_inSize);
258                 count = nbytes;
259         }
260 
261         if (copy_to_user(buf, req->uc_data, count)) {
262                 retval = -EFAULT;
263                 goto free_out;
264         }
265         
266         /* If request was not a signal, enqueue and don't free */
267         if (req->uc_opcode != CODA_SIGNAL) {
268                 req->uc_flags |= REQ_READ;
269                 list_add(&(req->uc_chain), vcp->vc_processing.prev);
270                 goto out;
271         }
272 
273         CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", 
274                         req->uc_opcode, req->uc_unique);
275 
276 free_out:
277         CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
278         CODA_FREE(req, sizeof(struct upc_req));
279 out:
280         unlock_kernel();
281         return (count ? count : retval);
282 }
283 
284 static int coda_psdev_open(struct inode * inode, struct file * file)
285 {
286         struct venus_comm *vcp;
287         int idx;
288         ENTRY;
289 
290         lock_kernel();
291         idx = MINOR(inode->i_rdev);
292         if(idx >= MAX_CODADEVS)
293                 return -ENODEV;
294 
295         vcp = &coda_comms[idx];
296         if(vcp->vc_inuse)
297                 return -EBUSY;
298         
299         if (!vcp->vc_inuse++) {
300                 INIT_LIST_HEAD(&vcp->vc_pending);
301                 INIT_LIST_HEAD(&vcp->vc_processing);
302                 init_waitqueue_head(&vcp->vc_waitq);
303                 vcp->vc_sb = 0;
304                 vcp->vc_seq = 0;
305         }
306         
307         file->private_data = vcp;
308 
309         CDEBUG(D_PSDEV, "device %i - inuse: %d\n", idx, vcp->vc_inuse);
310 
311         EXIT;
312         unlock_kernel();
313         return 0;
314 }
315 
316 
317 static int coda_psdev_release(struct inode * inode, struct file * file)
318 {
319         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
320         struct upc_req *req;
321         struct list_head *lh, *next;
322         ENTRY;
323 
324         lock_kernel();
325         if ( !vcp->vc_inuse ) {
326                 unlock_kernel();
327                 printk("psdev_release: Not open.\n");
328                 return -1;
329         }
330 
331         CDEBUG(D_PSDEV, "psdev_release: inuse %d\n", vcp->vc_inuse);
332         if (--vcp->vc_inuse) {
333                 unlock_kernel();
334                 return 0;
335         }
336         
337         /* Wakeup clients so they can return. */
338         CDEBUG(D_PSDEV, "wake up pending clients\n");
339         lh = vcp->vc_pending.next;
340         next = lh;
341         while ( (lh = next) != &vcp->vc_pending) {
342                 next = lh->next;
343                 req = list_entry(lh, struct upc_req, uc_chain);
344                 /* Async requests need to be freed here */
345                 if (req->uc_flags & REQ_ASYNC) {
346                         CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
347                         CODA_FREE(req, (u_int)sizeof(struct upc_req));
348                         continue;
349                 }
350                 req->uc_flags |= REQ_ABORT;
351                 wake_up(&req->uc_sleep);
352         }
353         
354         lh = &vcp->vc_processing;
355         CDEBUG(D_PSDEV, "wake up processing clients\n");
356         while ( (lh = lh->next) != &vcp->vc_processing) {
357                 req = list_entry(lh, struct upc_req, uc_chain);
358                 req->uc_flags |= REQ_ABORT;
359                 wake_up(&req->uc_sleep);
360         }
361         CDEBUG(D_PSDEV, "Done.\n");
362 
363         EXIT;
364         unlock_kernel();
365         return 0;
366 }
367 
368 
369 static struct file_operations coda_psdev_fops = {
370         owner:          THIS_MODULE,
371         read:           coda_psdev_read,
372         write:          coda_psdev_write,
373         poll:           coda_psdev_poll,
374         ioctl:          coda_psdev_ioctl,
375         open:           coda_psdev_open,
376         release:        coda_psdev_release,
377 };
378 
379 static devfs_handle_t devfs_handle;
380 
381 static int init_coda_psdev(void)
382 {
383         if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev",
384                                  &coda_psdev_fops)) {
385               printk(KERN_ERR "coda_psdev: unable to get major %d\n", 
386                      CODA_PSDEV_MAJOR);
387               return -EIO;
388         }
389         devfs_handle = devfs_mk_dir (NULL, "coda", NULL);
390         devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE,
391                                CODA_PSDEV_MAJOR, 0,
392                                S_IFCHR | S_IRUSR | S_IWUSR,
393                                &coda_psdev_fops, NULL);
394 
395         coda_sysctl_init();
396 
397         return 0;
398 }
399 
400 
401 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
402 
403 static int __init init_coda(void)
404 {
405         int status;
406         printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.9, coda@cs.cmu.edu\n");
407 
408         
409         status = init_coda_psdev();
410         if ( status ) {
411                 printk("Problem (%d) in init_coda_psdev\n", status);
412                 return status;
413         }
414         
415         status = register_filesystem(&coda_fs_type);
416         if (status) {
417                 printk("coda: failed in init_coda_fs!\n");
418         }
419         return status;
420 }
421 
422 static void __exit exit_coda(void)
423 {
424         int err;
425 
426         ENTRY;
427 
428         err = unregister_filesystem(&coda_fs_type);
429         if ( err != 0 ) {
430                 printk("coda: failed to unregister filesystem\n");
431         }
432         devfs_unregister (devfs_handle);
433         devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
434         coda_sysctl_clean();
435 }
436 
437 module_init(init_coda);
438 module_exit(exit_coda);
439 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.