diff options
Diffstat (limited to 'drivers/scsi/osd/osd_uld.c')
-rw-r--r-- | drivers/scsi/osd/osd_uld.c | 260 |
1 files changed, 177 insertions, 83 deletions
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 0bdef3390902..0a90702b3d71 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c | |||
@@ -71,8 +71,7 @@ | |||
71 | #define SCSI_OSD_MAX_MINOR 64 | 71 | #define SCSI_OSD_MAX_MINOR 64 |
72 | 72 | ||
73 | static const char osd_name[] = "osd"; | 73 | static const char osd_name[] = "osd"; |
74 | static const char *osd_version_string = "open-osd 0.1.0"; | 74 | static const char *osd_version_string = "open-osd 0.2.0"; |
75 | const char osd_symlink[] = "scsi_osd"; | ||
76 | 75 | ||
77 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | 76 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); |
78 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); | 77 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); |
@@ -82,15 +81,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD); | |||
82 | 81 | ||
83 | struct osd_uld_device { | 82 | struct osd_uld_device { |
84 | int minor; | 83 | int minor; |
85 | struct kref kref; | 84 | struct device class_dev; |
86 | struct cdev cdev; | 85 | struct cdev cdev; |
87 | struct osd_dev od; | 86 | struct osd_dev od; |
87 | struct osd_dev_info odi; | ||
88 | struct gendisk *disk; | 88 | struct gendisk *disk; |
89 | struct device *class_member; | ||
90 | }; | 89 | }; |
91 | 90 | ||
92 | static void __uld_get(struct osd_uld_device *oud); | 91 | struct osd_dev_handle { |
93 | static void __uld_put(struct osd_uld_device *oud); | 92 | struct osd_dev od; |
93 | struct file *file; | ||
94 | struct osd_uld_device *oud; | ||
95 | } ; | ||
96 | |||
97 | static DEFINE_IDA(osd_minor_ida); | ||
98 | |||
99 | static struct class osd_uld_class = { | ||
100 | .owner = THIS_MODULE, | ||
101 | .name = "scsi_osd", | ||
102 | }; | ||
94 | 103 | ||
95 | /* | 104 | /* |
96 | * Char Device operations | 105 | * Char Device operations |
@@ -101,7 +110,7 @@ static int osd_uld_open(struct inode *inode, struct file *file) | |||
101 | struct osd_uld_device *oud = container_of(inode->i_cdev, | 110 | struct osd_uld_device *oud = container_of(inode->i_cdev, |
102 | struct osd_uld_device, cdev); | 111 | struct osd_uld_device, cdev); |
103 | 112 | ||
104 | __uld_get(oud); | 113 | get_device(&oud->class_dev); |
105 | /* cache osd_uld_device on file handle */ | 114 | /* cache osd_uld_device on file handle */ |
106 | file->private_data = oud; | 115 | file->private_data = oud; |
107 | OSD_DEBUG("osd_uld_open %p\n", oud); | 116 | OSD_DEBUG("osd_uld_open %p\n", oud); |
@@ -114,7 +123,7 @@ static int osd_uld_release(struct inode *inode, struct file *file) | |||
114 | 123 | ||
115 | OSD_DEBUG("osd_uld_release %p\n", file->private_data); | 124 | OSD_DEBUG("osd_uld_release %p\n", file->private_data); |
116 | file->private_data = NULL; | 125 | file->private_data = NULL; |
117 | __uld_put(oud); | 126 | put_device(&oud->class_dev); |
118 | return 0; | 127 | return 0; |
119 | } | 128 | } |
120 | 129 | ||
@@ -177,7 +186,7 @@ static const struct file_operations osd_fops = { | |||
177 | struct osd_dev *osduld_path_lookup(const char *name) | 186 | struct osd_dev *osduld_path_lookup(const char *name) |
178 | { | 187 | { |
179 | struct osd_uld_device *oud; | 188 | struct osd_uld_device *oud; |
180 | struct osd_dev *od; | 189 | struct osd_dev_handle *odh; |
181 | struct file *file; | 190 | struct file *file; |
182 | int error; | 191 | int error; |
183 | 192 | ||
@@ -186,8 +195,8 @@ struct osd_dev *osduld_path_lookup(const char *name) | |||
186 | return ERR_PTR(-EINVAL); | 195 | return ERR_PTR(-EINVAL); |
187 | } | 196 | } |
188 | 197 | ||
189 | od = kzalloc(sizeof(*od), GFP_KERNEL); | 198 | odh = kzalloc(sizeof(*odh), GFP_KERNEL); |
190 | if (!od) | 199 | if (unlikely(!odh)) |
191 | return ERR_PTR(-ENOMEM); | 200 | return ERR_PTR(-ENOMEM); |
192 | 201 | ||
193 | file = filp_open(name, O_RDWR, 0); | 202 | file = filp_open(name, O_RDWR, 0); |
@@ -203,33 +212,134 @@ struct osd_dev *osduld_path_lookup(const char *name) | |||
203 | 212 | ||
204 | oud = file->private_data; | 213 | oud = file->private_data; |
205 | 214 | ||
206 | *od = oud->od; | 215 | odh->od = oud->od; |
207 | od->file = file; | 216 | odh->file = file; |
217 | odh->oud = oud; | ||
208 | 218 | ||
209 | return od; | 219 | return &odh->od; |
210 | 220 | ||
211 | close_file: | 221 | close_file: |
212 | fput(file); | 222 | fput(file); |
213 | free_od: | 223 | free_od: |
214 | kfree(od); | 224 | kfree(odh); |
215 | return ERR_PTR(error); | 225 | return ERR_PTR(error); |
216 | } | 226 | } |
217 | EXPORT_SYMBOL(osduld_path_lookup); | 227 | EXPORT_SYMBOL(osduld_path_lookup); |
218 | 228 | ||
219 | void osduld_put_device(struct osd_dev *od) | 229 | static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len, |
230 | const u8 *a2, unsigned a2_len) | ||
220 | { | 231 | { |
232 | if (!a2_len) /* User string is Empty means don't care */ | ||
233 | return true; | ||
234 | |||
235 | if (a1_len != a2_len) | ||
236 | return false; | ||
237 | |||
238 | return 0 == memcmp(a1, a2, a1_len); | ||
239 | } | ||
240 | |||
241 | struct find_oud_t { | ||
242 | const struct osd_dev_info *odi; | ||
243 | struct device *dev; | ||
244 | struct osd_uld_device *oud; | ||
245 | } ; | ||
246 | |||
247 | int _mach_odi(struct device *dev, void *find_data) | ||
248 | { | ||
249 | struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, | ||
250 | class_dev); | ||
251 | struct find_oud_t *fot = find_data; | ||
252 | const struct osd_dev_info *odi = fot->odi; | ||
253 | |||
254 | if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, | ||
255 | odi->systemid, odi->systemid_len) && | ||
256 | _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, | ||
257 | odi->osdname, odi->osdname_len)) { | ||
258 | OSD_DEBUG("found device sysid_len=%d osdname=%d\n", | ||
259 | odi->systemid_len, odi->osdname_len); | ||
260 | fot->oud = oud; | ||
261 | return 1; | ||
262 | } else { | ||
263 | return 0; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /* osduld_info_lookup - Loop through all devices, return the requested osd_dev. | ||
268 | * | ||
269 | * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't | ||
270 | * care. .e.g if they're both zero /dev/osd0 is returned. | ||
271 | */ | ||
272 | struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi) | ||
273 | { | ||
274 | struct find_oud_t find = {.odi = odi}; | ||
275 | |||
276 | find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi); | ||
277 | if (likely(find.dev)) { | ||
278 | struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL); | ||
279 | |||
280 | if (unlikely(!odh)) { | ||
281 | put_device(find.dev); | ||
282 | return ERR_PTR(-ENOMEM); | ||
283 | } | ||
221 | 284 | ||
285 | odh->od = find.oud->od; | ||
286 | odh->oud = find.oud; | ||
287 | |||
288 | return &odh->od; | ||
289 | } | ||
290 | |||
291 | return ERR_PTR(-ENODEV); | ||
292 | } | ||
293 | EXPORT_SYMBOL(osduld_info_lookup); | ||
294 | |||
295 | void osduld_put_device(struct osd_dev *od) | ||
296 | { | ||
222 | if (od && !IS_ERR(od)) { | 297 | if (od && !IS_ERR(od)) { |
223 | struct osd_uld_device *oud = od->file->private_data; | 298 | struct osd_dev_handle *odh = |
299 | container_of(od, struct osd_dev_handle, od); | ||
300 | struct osd_uld_device *oud = odh->oud; | ||
224 | 301 | ||
225 | BUG_ON(od->scsi_device != oud->od.scsi_device); | 302 | BUG_ON(od->scsi_device != oud->od.scsi_device); |
226 | 303 | ||
227 | fput(od->file); | 304 | /* If scsi has released the device (logout), and exofs has last |
228 | kfree(od); | 305 | * reference on oud it will be freed by above osd_uld_release |
306 | * within fput below. But this will oops in cdev_release which | ||
307 | * is called after the fops->release. A get_/put_ pair makes | ||
308 | * sure we have a cdev for the duration of fput | ||
309 | */ | ||
310 | if (odh->file) { | ||
311 | get_device(&oud->class_dev); | ||
312 | fput(odh->file); | ||
313 | } | ||
314 | put_device(&oud->class_dev); | ||
315 | kfree(odh); | ||
229 | } | 316 | } |
230 | } | 317 | } |
231 | EXPORT_SYMBOL(osduld_put_device); | 318 | EXPORT_SYMBOL(osduld_put_device); |
232 | 319 | ||
320 | const struct osd_dev_info *osduld_device_info(struct osd_dev *od) | ||
321 | { | ||
322 | struct osd_dev_handle *odh = | ||
323 | container_of(od, struct osd_dev_handle, od); | ||
324 | return &odh->oud->odi; | ||
325 | } | ||
326 | EXPORT_SYMBOL(osduld_device_info); | ||
327 | |||
328 | bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi) | ||
329 | { | ||
330 | struct osd_dev_handle *odh = | ||
331 | container_of(od, struct osd_dev_handle, od); | ||
332 | struct osd_uld_device *oud = odh->oud; | ||
333 | |||
334 | return (oud->odi.systemid_len == odi->systemid_len) && | ||
335 | _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, | ||
336 | odi->systemid, odi->systemid_len) && | ||
337 | (oud->odi.osdname_len == odi->osdname_len) && | ||
338 | _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, | ||
339 | odi->osdname, odi->osdname_len); | ||
340 | } | ||
341 | EXPORT_SYMBOL(osduld_device_same); | ||
342 | |||
233 | /* | 343 | /* |
234 | * Scsi Device operations | 344 | * Scsi Device operations |
235 | */ | 345 | */ |
@@ -250,14 +360,35 @@ static int __detect_osd(struct osd_uld_device *oud) | |||
250 | OSD_ERR("warning: scsi_test_unit_ready failed\n"); | 360 | OSD_ERR("warning: scsi_test_unit_ready failed\n"); |
251 | 361 | ||
252 | osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); | 362 | osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); |
253 | if (osd_auto_detect_ver(&oud->od, caps)) | 363 | if (osd_auto_detect_ver(&oud->od, caps, &oud->odi)) |
254 | return -ENODEV; | 364 | return -ENODEV; |
255 | 365 | ||
256 | return 0; | 366 | return 0; |
257 | } | 367 | } |
258 | 368 | ||
259 | static struct class *osd_sysfs_class; | 369 | static void __remove(struct device *dev) |
260 | static DEFINE_IDA(osd_minor_ida); | 370 | { |
371 | struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, | ||
372 | class_dev); | ||
373 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
374 | |||
375 | kfree(oud->odi.osdname); | ||
376 | |||
377 | if (oud->cdev.owner) | ||
378 | cdev_del(&oud->cdev); | ||
379 | |||
380 | osd_dev_fini(&oud->od); | ||
381 | scsi_device_put(scsi_device); | ||
382 | |||
383 | OSD_INFO("osd_remove %s\n", | ||
384 | oud->disk ? oud->disk->disk_name : NULL); | ||
385 | |||
386 | if (oud->disk) | ||
387 | put_disk(oud->disk); | ||
388 | ida_remove(&osd_minor_ida, oud->minor); | ||
389 | |||
390 | kfree(oud); | ||
391 | } | ||
261 | 392 | ||
262 | static int osd_probe(struct device *dev) | 393 | static int osd_probe(struct device *dev) |
263 | { | 394 | { |
@@ -289,7 +420,6 @@ static int osd_probe(struct device *dev) | |||
289 | if (NULL == oud) | 420 | if (NULL == oud) |
290 | goto err_retract_minor; | 421 | goto err_retract_minor; |
291 | 422 | ||
292 | kref_init(&oud->kref); | ||
293 | dev_set_drvdata(dev, oud); | 423 | dev_set_drvdata(dev, oud); |
294 | oud->minor = minor; | 424 | oud->minor = minor; |
295 | 425 | ||
@@ -327,18 +457,25 @@ static int osd_probe(struct device *dev) | |||
327 | OSD_ERR("cdev_add failed\n"); | 457 | OSD_ERR("cdev_add failed\n"); |
328 | goto err_put_disk; | 458 | goto err_put_disk; |
329 | } | 459 | } |
330 | kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */ | 460 | |
331 | 461 | /* class device member */ | |
332 | /* class_member */ | 462 | oud->class_dev.devt = oud->cdev.dev; |
333 | oud->class_member = device_create(osd_sysfs_class, dev, | 463 | oud->class_dev.class = &osd_uld_class; |
334 | MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name); | 464 | oud->class_dev.parent = dev; |
335 | if (IS_ERR(oud->class_member)) { | 465 | oud->class_dev.release = __remove; |
336 | OSD_ERR("class_device_create failed\n"); | 466 | error = dev_set_name(&oud->class_dev, disk->disk_name); |
337 | error = PTR_ERR(oud->class_member); | 467 | if (error) { |
468 | OSD_ERR("dev_set_name failed => %d\n", error); | ||
469 | goto err_put_cdev; | ||
470 | } | ||
471 | |||
472 | error = device_register(&oud->class_dev); | ||
473 | if (error) { | ||
474 | OSD_ERR("device_register failed => %d\n", error); | ||
338 | goto err_put_cdev; | 475 | goto err_put_cdev; |
339 | } | 476 | } |
340 | 477 | ||
341 | dev_set_drvdata(oud->class_member, oud); | 478 | get_device(&oud->class_dev); |
342 | 479 | ||
343 | OSD_INFO("osd_probe %s\n", disk->disk_name); | 480 | OSD_INFO("osd_probe %s\n", disk->disk_name); |
344 | return 0; | 481 | return 0; |
@@ -367,54 +504,12 @@ static int osd_remove(struct device *dev) | |||
367 | scsi_device); | 504 | scsi_device); |
368 | } | 505 | } |
369 | 506 | ||
370 | if (oud->class_member) | 507 | device_unregister(&oud->class_dev); |
371 | device_destroy(osd_sysfs_class, | ||
372 | MKDEV(SCSI_OSD_MAJOR, oud->minor)); | ||
373 | 508 | ||
374 | /* We have 2 references to the cdev. One is released here | 509 | put_device(&oud->class_dev); |
375 | * and also takes down the /dev/osdX mapping. The second | ||
376 | * Will be released in __remove() after all users have released | ||
377 | * the osd_uld_device. | ||
378 | */ | ||
379 | if (oud->cdev.owner) | ||
380 | cdev_del(&oud->cdev); | ||
381 | |||
382 | __uld_put(oud); | ||
383 | return 0; | 510 | return 0; |
384 | } | 511 | } |
385 | 512 | ||
386 | static void __remove(struct kref *kref) | ||
387 | { | ||
388 | struct osd_uld_device *oud = container_of(kref, | ||
389 | struct osd_uld_device, kref); | ||
390 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
391 | |||
392 | /* now let delete the char_dev */ | ||
393 | kobject_put(&oud->cdev.kobj); | ||
394 | |||
395 | osd_dev_fini(&oud->od); | ||
396 | scsi_device_put(scsi_device); | ||
397 | |||
398 | OSD_INFO("osd_remove %s\n", | ||
399 | oud->disk ? oud->disk->disk_name : NULL); | ||
400 | |||
401 | if (oud->disk) | ||
402 | put_disk(oud->disk); | ||
403 | |||
404 | ida_remove(&osd_minor_ida, oud->minor); | ||
405 | kfree(oud); | ||
406 | } | ||
407 | |||
408 | static void __uld_get(struct osd_uld_device *oud) | ||
409 | { | ||
410 | kref_get(&oud->kref); | ||
411 | } | ||
412 | |||
413 | static void __uld_put(struct osd_uld_device *oud) | ||
414 | { | ||
415 | kref_put(&oud->kref, __remove); | ||
416 | } | ||
417 | |||
418 | /* | 513 | /* |
419 | * Global driver and scsi registration | 514 | * Global driver and scsi registration |
420 | */ | 515 | */ |
@@ -432,11 +527,10 @@ static int __init osd_uld_init(void) | |||
432 | { | 527 | { |
433 | int err; | 528 | int err; |
434 | 529 | ||
435 | osd_sysfs_class = class_create(THIS_MODULE, osd_symlink); | 530 | err = class_register(&osd_uld_class); |
436 | if (IS_ERR(osd_sysfs_class)) { | 531 | if (err) { |
437 | OSD_ERR("Unable to register sysfs class => %ld\n", | 532 | OSD_ERR("Unable to register sysfs class => %d\n", err); |
438 | PTR_ERR(osd_sysfs_class)); | 533 | return err; |
439 | return PTR_ERR(osd_sysfs_class); | ||
440 | } | 534 | } |
441 | 535 | ||
442 | err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), | 536 | err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), |
@@ -459,7 +553,7 @@ static int __init osd_uld_init(void) | |||
459 | err_out_chrdev: | 553 | err_out_chrdev: |
460 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | 554 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); |
461 | err_out: | 555 | err_out: |
462 | class_destroy(osd_sysfs_class); | 556 | class_unregister(&osd_uld_class); |
463 | return err; | 557 | return err; |
464 | } | 558 | } |
465 | 559 | ||
@@ -467,7 +561,7 @@ static void __exit osd_uld_exit(void) | |||
467 | { | 561 | { |
468 | scsi_unregister_driver(&osd_driver.gendrv); | 562 | scsi_unregister_driver(&osd_driver.gendrv); |
469 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | 563 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); |
470 | class_destroy(osd_sysfs_class); | 564 | class_unregister(&osd_uld_class); |
471 | OSD_INFO("UNLOADED %s\n", osd_version_string); | 565 | OSD_INFO("UNLOADED %s\n", osd_version_string); |
472 | } | 566 | } |
473 | 567 | ||