]> git.proxmox.com Git - mirror_spl-debian.git/blame - modules/spl/spl-module.c
Breaking the world for a little bit. If anyone is going to continue
[mirror_spl-debian.git] / modules / spl / spl-module.c
CommitLineData
715f6251 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
4e62fd41 27#include <sys/sysmacros.h>
28#include <sys/sunddi.h>
4e62fd41 29
937879f1 30#ifdef DEBUG_SUBSYSTEM
31#undef DEBUG_SUBSYSTEM
32#endif
33
34#define DEBUG_SUBSYSTEM S_MODULE
35
4e62fd41 36static spinlock_t dev_info_lock = SPIN_LOCK_UNLOCKED;
37static LIST_HEAD(dev_info_list);
38
39static struct dev_info *
40get_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;
51out:
52 spin_unlock(&dev_info_lock);
53 return di;
54}
55
56static int
57mod_generic_ioctl(struct inode *ino, struct file *filp,
58 unsigned int cmd, unsigned long arg)
59{
60 struct dev_info *di;
61 int rc, flag = 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 flag, cr, &rvalp);
71 return rc;
72}
73
74int
75__ddi_create_minor_node(dev_info_t *di, char *name, int spec_type,
76 minor_t minor_num, char *node_type,
77 int flag, struct module *mod)
78{
79 struct cdev *cdev;
80 struct dev_ops *dev_ops;
81 struct cb_ops *cb_ops;
82 struct file_operations *fops;
83 int rc;
937879f1 84 ENTRY;
4e62fd41 85
937879f1 86 ASSERT(spec_type == S_IFCHR);
87 ASSERT(minor_num < di->di_minors);
88 ASSERT(!strcmp(node_type, DDI_PSEUDO));
89 ASSERT(flag == 0);
4e62fd41 90
e966e04f 91 fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
4e62fd41 92 if (fops == NULL)
937879f1 93 RETURN(DDI_FAILURE);
4e62fd41 94
95 cdev = cdev_alloc();
96 if (cdev == NULL) {
97 kfree(fops);
937879f1 98 RETURN(DDI_FAILURE);
4e62fd41 99 }
100
101 cdev->ops = fops;
102
103 mutex_enter(&di->di_lock);
104 dev_ops = di->di_ops;
937879f1 105 ASSERT(dev_ops);
4e62fd41 106 cb_ops = di->di_ops->devo_cb_ops;
937879f1 107 ASSERT(cb_ops);
4e62fd41 108
109 /* Setup the fops to cb_ops mapping */
110 fops->owner = mod;
111 if (cb_ops->cb_ioctl)
112 fops->ioctl = mod_generic_ioctl;
113
114#if 0
115 if (cb_ops->cb_open)
116 fops->open = mod_generic_open;
117
118 if (cb_ops->cb_close)
119 fops->release = mod_generic_close;
120
121 if (cb_ops->cb_read)
122 fops->read = mod_generic_read;
123
124 if (cb_ops->cb_write)
125 fops->write = mod_generic_write;
126#endif
127 /* XXX: Currently unsupported operations */
937879f1 128 ASSERT(cb_ops->cb_open == NULL);
129 ASSERT(cb_ops->cb_close == NULL);
130 ASSERT(cb_ops->cb_read == NULL);
131 ASSERT(cb_ops->cb_write == NULL);
132 ASSERT(cb_ops->cb_strategy == NULL);
133 ASSERT(cb_ops->cb_print == NULL);
134 ASSERT(cb_ops->cb_dump == NULL);
135 ASSERT(cb_ops->cb_devmap == NULL);
136 ASSERT(cb_ops->cb_mmap == NULL);
137 ASSERT(cb_ops->cb_segmap == NULL);
138 ASSERT(cb_ops->cb_chpoll == NULL);
139 ASSERT(cb_ops->cb_prop_op == NULL);
140 ASSERT(cb_ops->cb_str == NULL);
141 ASSERT(cb_ops->cb_aread == NULL);
142 ASSERT(cb_ops->cb_awrite == NULL);
4e62fd41 143
144 di->di_minor = minor_num;
145 di->di_dev = MKDEV(di->di_major, di->di_minor);
146
147 rc = cdev_add(cdev, di->di_dev, 1);
148 if (rc) {
937879f1 149 CERROR("Error adding cdev, %d\n", rc);
4e62fd41 150 kfree(fops);
151 cdev_del(cdev);
152 mutex_exit(&di->di_lock);
937879f1 153 RETURN(DDI_FAILURE);
4e62fd41 154 }
155
156 di->di_class = class_create(THIS_MODULE, name);
157 if (IS_ERR(di->di_class)) {
158 rc = PTR_ERR(di->di_class);
937879f1 159 CERROR("Error creating %s class, %d\n", name, rc);
4e62fd41 160 kfree(fops);
161 cdev_del(di->di_cdev);
162 mutex_exit(&di->di_lock);
937879f1 163 RETURN(DDI_FAILURE);
4e62fd41 164 }
165
336bb0c0 166 /* Do not append a 0 to devices with minor nums of 0 */
167 if (di->di_minor == 0) {
168 class_device_create(di->di_class, NULL, di->di_dev,
169 NULL, "%s", name);
170 } else {
171 class_device_create(di->di_class, NULL, di->di_dev,
172 NULL, "%s%d", name, di->di_minor);
173 }
4e62fd41 174
175 di->di_cdev = cdev;
176
177 spin_lock(&dev_info_lock);
178 list_add(&di->di_list, &dev_info_list);
179 spin_unlock(&dev_info_lock);
180
181 mutex_exit(&di->di_lock);
182
937879f1 183 RETURN(DDI_SUCCESS);
4e62fd41 184}
185EXPORT_SYMBOL(__ddi_create_minor_node);
186
187static void
336bb0c0 188__ddi_remove_minor_node_locked(dev_info_t *di, char *name)
4e62fd41 189{
336bb0c0 190 if (di->di_class) {
191 class_device_destroy(di->di_class, di->di_dev);
192 class_destroy(di->di_class);
193
194 di->di_class = NULL;
195 di->di_dev = 0;
196 }
197
198 if (di->di_cdev) {
199 cdev_del(di->di_cdev);
200 di->di_cdev = NULL;
201 }
4e62fd41 202
203 spin_lock(&dev_info_lock);
336bb0c0 204 list_del_init(&di->di_list);
4e62fd41 205 spin_unlock(&dev_info_lock);
206}
207
208void
209__ddi_remove_minor_node(dev_info_t *di, char *name)
210{
937879f1 211 ENTRY;
4e62fd41 212 mutex_enter(&di->di_lock);
336bb0c0 213 __ddi_remove_minor_node_locked(di, name);
4e62fd41 214 mutex_exit(&di->di_lock);
937879f1 215 EXIT;
4e62fd41 216}
217EXPORT_SYMBOL(ddi_remove_minor_node);
218
219#if 0
220static int
221mod_generic_open(struct inode *, struct file *)
222{
223 open(dev_t *devp, int flag, int otyp, cred_t *credp);
224}
225
226static int
227mod_generic_close(struct inode *, struct file *)
228{
229 close(dev_t dev, int flag, int otyp, cred_t *credp);
230}
231
232static ssize_t
233mod_generic_read(struct file *, char __user *, size_t, loff_t *)
234{
235 read(dev_t dev, struct uio *uiop, cred_t *credp);
236}
237
238static ssize_t
239mod_generic_write(struct file *, const char __user *, size_t, loff_t *)
240{
241 write(dev_t dev, struct uio *uiop, cred_t *credp);
242}
243#endif
244
245static struct dev_info *
246dev_info_alloc(major_t major, minor_t minors, struct dev_ops *ops) {
247 struct dev_info *di;
248
249 di = kmalloc(sizeof(struct dev_info), GFP_KERNEL);
250 if (di == NULL)
251 return NULL;
252
253 mutex_init(&di->di_lock, NULL, MUTEX_DEFAULT, NULL);
254 INIT_LIST_HEAD(&di->di_list);
255 di->di_ops = ops;
256 di->di_class = NULL;
257 di->di_cdev = NULL;
258 di->di_major = major;
259 di->di_minor = 0;
260 di->di_minors = minors;
261 di->di_dev = 0;
262
263 return di;
264}
265
266static void
267dev_info_free(struct dev_info *di)
268{
269 mutex_enter(&di->di_lock);
336bb0c0 270 __ddi_remove_minor_node_locked(di, NULL);
4e62fd41 271 mutex_exit(&di->di_lock);
272 mutex_destroy(&di->di_lock);
273 kfree(di);
274}
275
276int
277__mod_install(struct modlinkage *modlp)
278{
279 struct modldrv *drv = modlp->ml_modldrv;
280 struct dev_info *di;
281 int rc;
937879f1 282 ENTRY;
4e62fd41 283
284 di = dev_info_alloc(modlp->ml_major, modlp->ml_minors,
285 drv->drv_dev_ops);
286 if (di == NULL)
937879f1 287 RETURN(ENOMEM);
4e62fd41 288
289 /* XXX: Really we need to be calling devo_probe if it's available
290 * and then calling devo_attach for each device discovered. However
291 * for now we just call it once and let the app sort it out.
292 */
293 rc = drv->drv_dev_ops->devo_attach(di, DDI_ATTACH);
294 if (rc != DDI_SUCCESS) {
295 dev_info_free(di);
937879f1 296 RETURN(rc);
4e62fd41 297 }
298
299 drv->drv_dev_info = di;
300
937879f1 301 RETURN(DDI_SUCCESS);
4e62fd41 302}
303EXPORT_SYMBOL(__mod_install);
304
305int
306__mod_remove(struct modlinkage *modlp)
307{
308 struct modldrv *drv = modlp->ml_modldrv;
309 struct dev_info *di = drv->drv_dev_info;
310 int rc;
937879f1 311 ENTRY;
4e62fd41 312
313 rc = drv->drv_dev_ops->devo_detach(di, DDI_DETACH);
314 if (rc != DDI_SUCCESS)
937879f1 315 RETURN(rc);
4e62fd41 316
317 dev_info_free(di);
318 drv->drv_dev_info = NULL;
319
937879f1 320 RETURN(DDI_SUCCESS);
4e62fd41 321}
322EXPORT_SYMBOL(__mod_remove);
323
324int
325ldi_ident_from_mod(struct modlinkage *modlp, ldi_ident_t *lip)
326{
327 ldi_ident_t li;
937879f1 328 ENTRY;
4e62fd41 329
937879f1 330 ASSERT(modlp);
331 ASSERT(lip);
4e62fd41 332
333 li = kmalloc(sizeof(struct ldi_ident), GFP_KERNEL);
334 if (li == NULL)
937879f1 335 RETURN(ENOMEM);
4e62fd41 336
337 li->li_dev = MKDEV(modlp->ml_major, 0);
338 *lip = li;
339
937879f1 340 RETURN(0);
4e62fd41 341}
342EXPORT_SYMBOL(ldi_ident_from_mod);
343
344void
345ldi_ident_release(ldi_ident_t lip)
346{
937879f1 347 ENTRY;
348 ASSERT(lip);
4e62fd41 349 kfree(lip);
937879f1 350 EXIT;
4e62fd41 351}
352EXPORT_SYMBOL(ldi_ident_release);