]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/spl/spl-module.c
Register a basic compat ioctl handler (32 vs 64 bit compat)
[mirror_spl-debian.git] / module / spl / spl-module.c
1 /*
2 * This file is part of the SPL: Solaris Porting Layer.
3 *
4 * Copyright (c) 2008 Lawrence Livermore National Security, LLC.
5 * Produced at Lawrence Livermore National Laboratory
6 * Written by:
7 * Brian Behlendorf <behlendorf1@llnl.gov>,
8 * Herb Wartens <wartens2@llnl.gov>,
9 * Jim Garlick <garlick@llnl.gov>
10 * UCRL-CODE-235197
11 *
12 * This is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
27 #include <sys/sysmacros.h>
28 #include <sys/sunddi.h>
29
30 #ifdef DEBUG_SUBSYSTEM
31 #undef DEBUG_SUBSYSTEM
32 #endif
33
34 #define DEBUG_SUBSYSTEM S_MODULE
35
36 static spinlock_t dev_info_lock = SPIN_LOCK_UNLOCKED;
37 static LIST_HEAD(dev_info_list);
38
39 static struct dev_info *
40 get_dev_info(dev_t dev)
41 {
42 struct dev_info *di;
43
44 spin_lock(&dev_info_lock);
45
46 list_for_each_entry(di, &dev_info_list, di_list)
47 if (di->di_dev == dev)
48 goto out;
49
50 di = NULL;
51 out:
52 spin_unlock(&dev_info_lock);
53 return di;
54 }
55
56 static int
57 mod_generic_ioctl(struct inode *ino, struct file *file,
58 unsigned int cmd, unsigned long arg)
59 {
60 struct dev_info *di;
61 int rc, flags = 0, rvalp = 0;
62 cred_t *cr = NULL;
63
64 di = get_dev_info(MKDEV(imajor(ino), iminor(ino)));
65 if (di == NULL)
66 return EINVAL;
67
68 rc = di->di_ops->devo_cb_ops->cb_ioctl(di->di_dev,
69 (int)cmd, (intptr_t)arg,
70 flags, cr, &rvalp);
71 return rc;
72 }
73
74 #ifdef CONFIG_COMPAT
75 /* Compatibility handler for ioctls from 32-bit ELF binaries */
76 static long
77 mod_generic_compat_ioctl(struct file *file,
78 unsigned int cmd, unsigned long arg)
79 {
80 return mod_generic_ioctl(file->f_dentry->d_inode, file, cmd, arg);
81 }
82 #endif /* CONFIG_COMPAT */
83
84 int
85 __ddi_create_minor_node(dev_info_t *di, char *name, int spec_type,
86 minor_t minor_num, char *node_type,
87 int flags, struct module *mod)
88 {
89 struct cdev *cdev;
90 struct dev_ops *dev_ops;
91 struct cb_ops *cb_ops;
92 struct file_operations *fops;
93 int rc;
94 ENTRY;
95
96 ASSERT(spec_type == S_IFCHR);
97 ASSERT(minor_num < di->di_minors);
98 ASSERT(!strcmp(node_type, DDI_PSEUDO));
99
100 fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
101 if (fops == NULL)
102 RETURN(DDI_FAILURE);
103
104 cdev = cdev_alloc();
105 if (cdev == NULL) {
106 kfree(fops);
107 RETURN(DDI_FAILURE);
108 }
109
110 cdev->ops = fops;
111
112 mutex_enter(&di->di_lock);
113 dev_ops = di->di_ops;
114 ASSERT(dev_ops);
115 cb_ops = di->di_ops->devo_cb_ops;
116 ASSERT(cb_ops);
117
118 /* Setup the fops to cb_ops mapping */
119 fops->owner = mod;
120 if (cb_ops->cb_ioctl) {
121 fops->ioctl = mod_generic_ioctl;
122 #ifdef CONFIG_COMPAT
123 fops->compat_ioctl = mod_generic_compat_ioctl;
124 #endif
125 }
126
127 #if 0
128 if (cb_ops->cb_open)
129 fops->open = mod_generic_open;
130
131 if (cb_ops->cb_close)
132 fops->release = mod_generic_close;
133
134 if (cb_ops->cb_read)
135 fops->read = mod_generic_read;
136
137 if (cb_ops->cb_write)
138 fops->write = mod_generic_write;
139 #endif
140 /* XXX: Currently unsupported operations */
141 ASSERT(cb_ops->cb_open == NULL);
142 ASSERT(cb_ops->cb_close == NULL);
143 ASSERT(cb_ops->cb_read == NULL);
144 ASSERT(cb_ops->cb_write == NULL);
145 ASSERT(cb_ops->cb_strategy == NULL);
146 ASSERT(cb_ops->cb_print == NULL);
147 ASSERT(cb_ops->cb_dump == NULL);
148 ASSERT(cb_ops->cb_devmap == NULL);
149 ASSERT(cb_ops->cb_mmap == NULL);
150 ASSERT(cb_ops->cb_segmap == NULL);
151 ASSERT(cb_ops->cb_chpoll == NULL);
152 ASSERT(cb_ops->cb_prop_op == NULL);
153 ASSERT(cb_ops->cb_str == NULL);
154 ASSERT(cb_ops->cb_aread == NULL);
155 ASSERT(cb_ops->cb_awrite == NULL);
156
157 snprintf(di->di_name, DDI_MAX_NAME_LEN-1, "/dev/%s", name);
158 di->di_cdev = cdev;
159 di->di_flags = flags;
160 di->di_minor = minor_num;
161 di->di_dev = MKDEV(di->di_major, di->di_minor);
162
163 rc = cdev_add(cdev, di->di_dev, 1);
164 if (rc) {
165 CERROR("Error adding cdev, %d\n", rc);
166 kfree(fops);
167 cdev_del(cdev);
168 mutex_exit(&di->di_lock);
169 RETURN(DDI_FAILURE);
170 }
171
172 spin_lock(&dev_info_lock);
173 list_add(&di->di_list, &dev_info_list);
174 spin_unlock(&dev_info_lock);
175
176 mutex_exit(&di->di_lock);
177
178 RETURN(DDI_SUCCESS);
179 }
180 EXPORT_SYMBOL(__ddi_create_minor_node);
181
182 static void
183 __ddi_remove_minor_node_locked(dev_info_t *di, char *name)
184 {
185 if (di->di_cdev) {
186 cdev_del(di->di_cdev);
187 di->di_cdev = NULL;
188 }
189
190 spin_lock(&dev_info_lock);
191 list_del_init(&di->di_list);
192 spin_unlock(&dev_info_lock);
193 }
194
195 void
196 __ddi_remove_minor_node(dev_info_t *di, char *name)
197 {
198 ENTRY;
199 mutex_enter(&di->di_lock);
200 __ddi_remove_minor_node_locked(di, name);
201 mutex_exit(&di->di_lock);
202 EXIT;
203 }
204 EXPORT_SYMBOL(__ddi_remove_minor_node);
205
206 int
207 ddi_quiesce_not_needed(dev_info_t *dip)
208 {
209 RETURN(DDI_SUCCESS);
210 }
211 EXPORT_SYMBOL(ddi_quiesce_not_needed);
212
213 #if 0
214 static int
215 mod_generic_open(struct inode *, struct file *)
216 {
217 open(dev_t *devp, int flags, int otyp, cred_t *credp);
218 }
219
220 static int
221 mod_generic_close(struct inode *, struct file *)
222 {
223 close(dev_t dev, int flags, int otyp, cred_t *credp);
224 }
225
226 static ssize_t
227 mod_generic_read(struct file *, char __user *, size_t, loff_t *)
228 {
229 read(dev_t dev, struct uio *uiop, cred_t *credp);
230 }
231
232 static ssize_t
233 mod_generic_write(struct file *, const char __user *, size_t, loff_t *)
234 {
235 write(dev_t dev, struct uio *uiop, cred_t *credp);
236 }
237 #endif
238
239 static struct dev_info *
240 dev_info_alloc(major_t major, minor_t minors, struct dev_ops *ops) {
241 struct dev_info *di;
242
243 di = kmalloc(sizeof(struct dev_info), GFP_KERNEL);
244 if (di == NULL)
245 return NULL;
246
247 mutex_init(&di->di_lock, NULL, MUTEX_DEFAULT, NULL);
248 INIT_LIST_HEAD(&di->di_list);
249 di->di_ops = ops;
250 di->di_class = NULL;
251 di->di_cdev = NULL;
252 di->di_major = major;
253 di->di_minor = 0;
254 di->di_minors = minors;
255 di->di_dev = 0;
256
257 return di;
258 }
259
260 static void
261 dev_info_free(struct dev_info *di)
262 {
263 mutex_enter(&di->di_lock);
264 __ddi_remove_minor_node_locked(di, NULL);
265 mutex_exit(&di->di_lock);
266 mutex_destroy(&di->di_lock);
267 kfree(di);
268 }
269
270 int
271 __mod_install(struct modlinkage *modlp)
272 {
273 struct modldrv *drv = modlp->ml_modldrv;
274 struct dev_info *di;
275 int rc;
276 ENTRY;
277
278 di = dev_info_alloc(modlp->ml_major, modlp->ml_minors,
279 drv->drv_dev_ops);
280 if (di == NULL)
281 RETURN(ENOMEM);
282
283 /* XXX: Really we need to be calling devo_probe if it's available
284 * and then calling devo_attach for each device discovered. However
285 * for now we just call it once and let the app sort it out.
286 */
287 rc = drv->drv_dev_ops->devo_attach(di, DDI_ATTACH);
288 if (rc != DDI_SUCCESS) {
289 dev_info_free(di);
290 RETURN(rc);
291 }
292
293 drv->drv_dev_info = di;
294
295 RETURN(DDI_SUCCESS);
296 }
297 EXPORT_SYMBOL(__mod_install);
298
299 int
300 __mod_mknod(char *name, char *type, int major, int minor)
301 {
302 char cmd[] = "/bin/mknod";
303 char major_str[8];
304 char minor_str[8];
305 char *argv[] = { cmd,
306 name,
307 type,
308 major_str,
309 minor_str,
310 NULL };
311 char *envp[] = { "HOME=/",
312 "TERM=linux",
313 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
314 NULL };
315
316 snprintf(major_str, 8, "%d", major);
317 snprintf(minor_str, 8, "%d", minor);
318
319 return call_usermodehelper(cmd, argv, envp, 1);
320 }
321 EXPORT_SYMBOL(__mod_mknod);
322
323 int
324 __mod_remove(struct modlinkage *modlp)
325 {
326 struct modldrv *drv = modlp->ml_modldrv;
327 struct dev_info *di = drv->drv_dev_info;
328 int rc;
329 ENTRY;
330
331 rc = drv->drv_dev_ops->devo_detach(di, DDI_DETACH);
332 if (rc != DDI_SUCCESS)
333 RETURN(rc);
334
335 dev_info_free(di);
336 drv->drv_dev_info = NULL;
337
338 RETURN(DDI_SUCCESS);
339 }
340 EXPORT_SYMBOL(__mod_remove);
341
342 int
343 ldi_ident_from_mod(struct modlinkage *modlp, ldi_ident_t *lip)
344 {
345 ldi_ident_t li;
346 ENTRY;
347
348 ASSERT(modlp);
349 ASSERT(lip);
350
351 li = kmalloc(sizeof(struct ldi_ident), GFP_KERNEL);
352 if (li == NULL)
353 RETURN(ENOMEM);
354
355 li->li_dev = MKDEV(modlp->ml_major, 0);
356 *lip = li;
357
358 RETURN(0);
359 }
360 EXPORT_SYMBOL(ldi_ident_from_mod);
361
362 void
363 ldi_ident_release(ldi_ident_t lip)
364 {
365 ENTRY;
366 ASSERT(lip);
367 kfree(lip);
368 EXIT;
369 }
370 EXPORT_SYMBOL(ldi_ident_release);