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