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