1/*
2 * usema.c: software semaphore driver (see IRIX's usema(7M))
3 * written 1997 Mike Shaver (shaver@neon.ingenia.ca)
4 *         1997 Miguel de Icaza (miguel@kernel.org)
5 *
6 * This file contains the implementation of /dev/usemaclone,
7 * the devices used by IRIX's us* semaphore routines.
8 *
9 * /dev/usemaclone is used to create a new semaphore device, and then
10 * the semaphore is manipulated via ioctls.
11 *
12 * At least for the zero-contention case, lock set and unset as well
13 * as semaphore P and V are done in userland, which makes things a
14 * little bit better.  I suspect that the ioctls are used to register
15 * the process as blocking, etc.
16 *
17 * Much inspiration and structure stolen from Miguel's shmiq work.
18 *
19 * For more information:
20 * usema(7m), usinit(3p), usnewsema(3p)
21 * /usr/include/sys/usioctl.h
22 *
23 */
24#include <linux/fs.h>
25#include <linux/miscdevice.h>
26#include <linux/sched.h>
27#include <linux/file.h>
28#include <linux/major.h>
29#include <linux/poll.h>
30#include <linux/string.h>
31#include <linux/dcache.h>
32#include <linux/mm.h>
33#include <linux/module.h>
34#include <linux/slab.h>
35#include <linux/smp_lock.h>
36#include "usema.h"
37
38#include <asm/usioctl.h>
39#include <asm/mman.h>
40#include <asm/uaccess.h>
41
42struct irix_usema {
43	struct file *filp;
44	wait_queue_head_t proc_list;
45};
46
47
48static int
49sgi_usema_attach (usattach_t * attach, struct irix_usema *usema)
50{
51	int newfd;
52	newfd = get_unused_fd();
53	if (newfd < 0)
54		return newfd;
55
56	get_file(usema->filp);
57	fd_install(newfd, usema->filp);
58	/* Is that it? */
59	printk("UIOCATTACHSEMA: new usema fd is %d", newfd);
60	return newfd;
61}
62
63static int
64sgi_usemaclone_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
65		unsigned long arg)
66{
67	struct irix_usema *usema = file->private_data;
68	int retval;
69
70	printk("[%s:%d] wants ioctl 0x%xd (arg 0x%lx)",
71	       current->comm, current->pid, cmd, arg);
72
73	switch(cmd) {
74	case UIOCATTACHSEMA: {
75		/* They pass us information about the semaphore to
76		   which they wish to be attached, and we create&return
77		   a new fd corresponding to the appropriate semaphore.
78		   */
79		usattach_t *attach = (usattach_t *)arg;
80		retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
81		if (retval) {
82			printk("[%s:%d] sgi_usema_ioctl(UIOCATTACHSEMA): "
83			       "verify_area failure",
84			       current->comm, current->pid);
85			return retval;
86		}
87		if (usema == 0)
88			return -EINVAL;
89
90		printk("UIOCATTACHSEMA: attaching usema %p to process %d\n",
91		       usema, current->pid);
92		return sgi_usema_attach(attach, usema);
93		break;
94	}
95	case UIOCABLOCK:
96	case UIOCNOIBLOCK:
97	case UIOCBLOCK: {
98		/* Block this process on the semaphore */
99		usattach_t *attach = (usattach_t *)arg;
100
101		retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
102		if (retval) {
103			printk("[%s:%d] sgi_usema_ioctl(UIOC*BLOCK): "
104			       "verify_area failure",
105			       current->comm, current->pid);
106			return retval;
107		}
108		printk("UIOC*BLOCK: putting process %d to sleep on usema %p",
109		       current->pid, usema);
110		if (cmd == UIOCNOIBLOCK)
111			interruptible_sleep_on(&usema->proc_list);
112		else
113			sleep_on(&usema->proc_list);
114		return 0;
115	}
116	case UIOCAUNBLOCK:
117	case UIOCUNBLOCK: {
118		/* Wake up all process waiting on this semaphore */
119		usattach_t *attach = (usattach_t *)arg;
120
121		retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
122		if (retval) {
123			printk("[%s:%d] sgi_usema_ioctl(UIOC*BLOCK): "
124			       "verify_area failure",
125			       current->comm, current->pid);
126			return retval;
127		}
128
129		printk("[%s:%d] releasing usema %p",
130		       current->comm, current->pid, usema);
131		wake_up(&usema->proc_list);
132		return 0;
133	}
134	}
135	return -ENOSYS;
136}
137
138static unsigned int
139sgi_usemaclone_poll(struct file *filp, poll_table *wait)
140{
141	struct irix_usema *usema = filp->private_data;
142
143	printk("[%s:%d] wants to poll usema %p",
144	       current->comm, current->pid, usema);
145
146	return 0;
147}
148
149static int
150sgi_usemaclone_open(struct inode *inode, struct file *filp)
151{
152	struct irix_usema *usema;
153
154	usema = kmalloc (sizeof (struct irix_usema), GFP_KERNEL);
155	if (!usema)
156		return -ENOMEM;
157
158	usema->filp        = filp;
159	init_waitqueue_head(&usema->proc_list);
160	filp->private_data = usema;
161
162	return 0;
163}
164
165struct file_operations sgi_usemaclone_fops = {
166	.poll		= sgi_usemaclone_poll,
167	.ioctl		= sgi_usemaclone_ioctl,
168	.open		= sgi_usemaclone_open,
169};
170
171static struct miscdevice dev_usemaclone = {
172	.minor	= SGI_USEMACLONE,
173	.name	= "usemaclone",
174	.fops	= &sgi_usemaclone_fops,
175};
176
177void
178usema_init(void)
179{
180	printk(KERN_INFO "usemaclone misc device registered (minor: %d)\n",
181	       SGI_USEMACLONE);
182	misc_register(&dev_usemaclone);
183}
184
185EXPORT_SYMBOL(usema_init);
186