diff options
author | Dan Williams <dan.j.williams@intel.com> | 2009-09-08 20:55:21 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-09-08 20:55:21 -0400 |
commit | bbb20089a3275a19e475dbc21320c3742e3ca423 (patch) | |
tree | 216fdc1cbef450ca688135c5b8969169482d9a48 /drivers/scsi/scsi_devinfo.c | |
parent | 3e48e656903e9fd8bc805c6a2c4264d7808d315b (diff) | |
parent | 657a77fa7284d8ae28dfa48f1dc5d919bf5b2843 (diff) |
Merge branch 'dmaengine' into async-tx-next
Conflicts:
crypto/async_tx/async_xor.c
drivers/dma/ioat/dma_v2.h
drivers/dma/ioat/pci.c
drivers/md/raid5.c
Diffstat (limited to 'drivers/scsi/scsi_devinfo.c')
-rw-r--r-- | drivers/scsi/scsi_devinfo.c | 248 |
1 files changed, 226 insertions, 22 deletions
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index b13481369642..93c2622cb969 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c | |||
@@ -24,6 +24,13 @@ struct scsi_dev_info_list { | |||
24 | unsigned compatible; /* for use with scsi_static_device_list entries */ | 24 | unsigned compatible; /* for use with scsi_static_device_list entries */ |
25 | }; | 25 | }; |
26 | 26 | ||
27 | struct scsi_dev_info_list_table { | ||
28 | struct list_head node; /* our node for being on the master list */ | ||
29 | struct list_head scsi_dev_info_list; /* head of dev info list */ | ||
30 | const char *name; /* name of list for /proc (NULL for global) */ | ||
31 | int key; /* unique numeric identifier */ | ||
32 | }; | ||
33 | |||
27 | 34 | ||
28 | static const char spaces[] = " "; /* 16 of them */ | 35 | static const char spaces[] = " "; /* 16 of them */ |
29 | static unsigned scsi_default_dev_flags; | 36 | static unsigned scsi_default_dev_flags; |
@@ -225,6 +232,7 @@ static struct { | |||
225 | {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, | 232 | {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, |
226 | {"IBM", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, | 233 | {"IBM", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, |
227 | {"SUN", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, | 234 | {"SUN", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, |
235 | {"DELL", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, | ||
228 | {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36}, | 236 | {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36}, |
229 | {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, | 237 | {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, |
230 | {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ | 238 | {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ |
@@ -246,6 +254,22 @@ static struct { | |||
246 | { NULL, NULL, NULL, 0 }, | 254 | { NULL, NULL, NULL, 0 }, |
247 | }; | 255 | }; |
248 | 256 | ||
257 | static struct scsi_dev_info_list_table *scsi_devinfo_lookup_by_key(int key) | ||
258 | { | ||
259 | struct scsi_dev_info_list_table *devinfo_table; | ||
260 | int found = 0; | ||
261 | |||
262 | list_for_each_entry(devinfo_table, &scsi_dev_info_list, node) | ||
263 | if (devinfo_table->key == key) { | ||
264 | found = 1; | ||
265 | break; | ||
266 | } | ||
267 | if (!found) | ||
268 | return ERR_PTR(-EINVAL); | ||
269 | |||
270 | return devinfo_table; | ||
271 | } | ||
272 | |||
249 | /* | 273 | /* |
250 | * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into | 274 | * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into |
251 | * devinfo vendor and model strings. | 275 | * devinfo vendor and model strings. |
@@ -295,7 +319,38 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, | |||
295 | static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, | 319 | static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, |
296 | char *strflags, int flags) | 320 | char *strflags, int flags) |
297 | { | 321 | { |
322 | return scsi_dev_info_list_add_keyed(compatible, vendor, model, | ||
323 | strflags, flags, | ||
324 | SCSI_DEVINFO_GLOBAL); | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * scsi_dev_info_list_add_keyed - add one dev_info list entry. | ||
329 | * @compatible: if true, null terminate short strings. Otherwise space pad. | ||
330 | * @vendor: vendor string | ||
331 | * @model: model (product) string | ||
332 | * @strflags: integer string | ||
333 | * @flags: if strflags NULL, use this flag value | ||
334 | * @key: specify list to use | ||
335 | * | ||
336 | * Description: | ||
337 | * Create and add one dev_info entry for @vendor, @model, | ||
338 | * @strflags or @flag in list specified by @key. If @compatible, | ||
339 | * add to the tail of the list, do not space pad, and set | ||
340 | * devinfo->compatible. The scsi_static_device_list entries are | ||
341 | * added with @compatible 1 and @clfags NULL. | ||
342 | * | ||
343 | * Returns: 0 OK, -error on failure. | ||
344 | **/ | ||
345 | int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, | ||
346 | char *strflags, int flags, int key) | ||
347 | { | ||
298 | struct scsi_dev_info_list *devinfo; | 348 | struct scsi_dev_info_list *devinfo; |
349 | struct scsi_dev_info_list_table *devinfo_table = | ||
350 | scsi_devinfo_lookup_by_key(key); | ||
351 | |||
352 | if (IS_ERR(devinfo_table)) | ||
353 | return PTR_ERR(devinfo_table); | ||
299 | 354 | ||
300 | devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); | 355 | devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); |
301 | if (!devinfo) { | 356 | if (!devinfo) { |
@@ -316,12 +371,15 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, | |||
316 | devinfo->compatible = compatible; | 371 | devinfo->compatible = compatible; |
317 | 372 | ||
318 | if (compatible) | 373 | if (compatible) |
319 | list_add_tail(&devinfo->dev_info_list, &scsi_dev_info_list); | 374 | list_add_tail(&devinfo->dev_info_list, |
375 | &devinfo_table->scsi_dev_info_list); | ||
320 | else | 376 | else |
321 | list_add(&devinfo->dev_info_list, &scsi_dev_info_list); | 377 | list_add(&devinfo->dev_info_list, |
378 | &devinfo_table->scsi_dev_info_list); | ||
322 | 379 | ||
323 | return 0; | 380 | return 0; |
324 | } | 381 | } |
382 | EXPORT_SYMBOL(scsi_dev_info_list_add_keyed); | ||
325 | 383 | ||
326 | /** | 384 | /** |
327 | * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. | 385 | * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. |
@@ -381,22 +439,48 @@ static int scsi_dev_info_list_add_str(char *dev_list) | |||
381 | * @model: model name | 439 | * @model: model name |
382 | * | 440 | * |
383 | * Description: | 441 | * Description: |
384 | * Search the scsi_dev_info_list for an entry matching @vendor and | 442 | * Search the global scsi_dev_info_list (specified by list zero) |
385 | * @model, if found, return the matching flags value, else return | 443 | * for an entry matching @vendor and @model, if found, return the |
386 | * the host or global default settings. Called during scan time. | 444 | * matching flags value, else return the host or global default |
445 | * settings. Called during scan time. | ||
387 | **/ | 446 | **/ |
388 | int scsi_get_device_flags(struct scsi_device *sdev, | 447 | int scsi_get_device_flags(struct scsi_device *sdev, |
389 | const unsigned char *vendor, | 448 | const unsigned char *vendor, |
390 | const unsigned char *model) | 449 | const unsigned char *model) |
391 | { | 450 | { |
451 | return scsi_get_device_flags_keyed(sdev, vendor, model, | ||
452 | SCSI_DEVINFO_GLOBAL); | ||
453 | } | ||
454 | |||
455 | |||
456 | /** | ||
457 | * get_device_flags_keyed - get device specific flags from the dynamic device list. | ||
458 | * @sdev: &scsi_device to get flags for | ||
459 | * @vendor: vendor name | ||
460 | * @model: model name | ||
461 | * @key: list to look up | ||
462 | * | ||
463 | * Description: | ||
464 | * Search the scsi_dev_info_list specified by @key for an entry | ||
465 | * matching @vendor and @model, if found, return the matching | ||
466 | * flags value, else return the host or global default settings. | ||
467 | * Called during scan time. | ||
468 | **/ | ||
469 | int scsi_get_device_flags_keyed(struct scsi_device *sdev, | ||
470 | const unsigned char *vendor, | ||
471 | const unsigned char *model, | ||
472 | int key) | ||
473 | { | ||
392 | struct scsi_dev_info_list *devinfo; | 474 | struct scsi_dev_info_list *devinfo; |
393 | unsigned int bflags; | 475 | struct scsi_dev_info_list_table *devinfo_table; |
476 | |||
477 | devinfo_table = scsi_devinfo_lookup_by_key(key); | ||
394 | 478 | ||
395 | bflags = sdev->sdev_bflags; | 479 | if (IS_ERR(devinfo_table)) |
396 | if (!bflags) | 480 | return PTR_ERR(devinfo_table); |
397 | bflags = scsi_default_dev_flags; | ||
398 | 481 | ||
399 | list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) { | 482 | list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list, |
483 | dev_info_list) { | ||
400 | if (devinfo->compatible) { | 484 | if (devinfo->compatible) { |
401 | /* | 485 | /* |
402 | * Behave like the older version of get_device_flags. | 486 | * Behave like the older version of get_device_flags. |
@@ -446,32 +530,89 @@ int scsi_get_device_flags(struct scsi_device *sdev, | |||
446 | return devinfo->flags; | 530 | return devinfo->flags; |
447 | } | 531 | } |
448 | } | 532 | } |
449 | return bflags; | 533 | /* nothing found, return nothing */ |
534 | if (key != SCSI_DEVINFO_GLOBAL) | ||
535 | return 0; | ||
536 | |||
537 | /* except for the global list, where we have an exception */ | ||
538 | if (sdev->sdev_bflags) | ||
539 | return sdev->sdev_bflags; | ||
540 | |||
541 | return scsi_default_dev_flags; | ||
450 | } | 542 | } |
543 | EXPORT_SYMBOL(scsi_get_device_flags_keyed); | ||
451 | 544 | ||
452 | #ifdef CONFIG_SCSI_PROC_FS | 545 | #ifdef CONFIG_SCSI_PROC_FS |
546 | struct double_list { | ||
547 | struct list_head *top; | ||
548 | struct list_head *bottom; | ||
549 | }; | ||
550 | |||
453 | static int devinfo_seq_show(struct seq_file *m, void *v) | 551 | static int devinfo_seq_show(struct seq_file *m, void *v) |
454 | { | 552 | { |
553 | struct double_list *dl = v; | ||
554 | struct scsi_dev_info_list_table *devinfo_table = | ||
555 | list_entry(dl->top, struct scsi_dev_info_list_table, node); | ||
455 | struct scsi_dev_info_list *devinfo = | 556 | struct scsi_dev_info_list *devinfo = |
456 | list_entry(v, struct scsi_dev_info_list, dev_info_list); | 557 | list_entry(dl->bottom, struct scsi_dev_info_list, |
558 | dev_info_list); | ||
559 | |||
560 | if (devinfo_table->scsi_dev_info_list.next == dl->bottom && | ||
561 | devinfo_table->name) | ||
562 | seq_printf(m, "[%s]:\n", devinfo_table->name); | ||
457 | 563 | ||
458 | seq_printf(m, "'%.8s' '%.16s' 0x%x\n", | 564 | seq_printf(m, "'%.8s' '%.16s' 0x%x\n", |
459 | devinfo->vendor, devinfo->model, devinfo->flags); | 565 | devinfo->vendor, devinfo->model, devinfo->flags); |
460 | return 0; | 566 | return 0; |
461 | } | 567 | } |
462 | 568 | ||
463 | static void * devinfo_seq_start(struct seq_file *m, loff_t *pos) | 569 | static void *devinfo_seq_start(struct seq_file *m, loff_t *ppos) |
464 | { | 570 | { |
465 | return seq_list_start(&scsi_dev_info_list, *pos); | 571 | struct double_list *dl = kmalloc(sizeof(*dl), GFP_KERNEL); |
572 | loff_t pos = *ppos; | ||
573 | |||
574 | if (!dl) | ||
575 | return NULL; | ||
576 | |||
577 | list_for_each(dl->top, &scsi_dev_info_list) { | ||
578 | struct scsi_dev_info_list_table *devinfo_table = | ||
579 | list_entry(dl->top, struct scsi_dev_info_list_table, | ||
580 | node); | ||
581 | list_for_each(dl->bottom, &devinfo_table->scsi_dev_info_list) | ||
582 | if (pos-- == 0) | ||
583 | return dl; | ||
584 | } | ||
585 | |||
586 | kfree(dl); | ||
587 | return NULL; | ||
466 | } | 588 | } |
467 | 589 | ||
468 | static void * devinfo_seq_next(struct seq_file *m, void *v, loff_t *pos) | 590 | static void *devinfo_seq_next(struct seq_file *m, void *v, loff_t *ppos) |
469 | { | 591 | { |
470 | return seq_list_next(v, &scsi_dev_info_list, pos); | 592 | struct double_list *dl = v; |
593 | struct scsi_dev_info_list_table *devinfo_table = | ||
594 | list_entry(dl->top, struct scsi_dev_info_list_table, node); | ||
595 | |||
596 | ++*ppos; | ||
597 | dl->bottom = dl->bottom->next; | ||
598 | while (&devinfo_table->scsi_dev_info_list == dl->bottom) { | ||
599 | dl->top = dl->top->next; | ||
600 | if (dl->top == &scsi_dev_info_list) { | ||
601 | kfree(dl); | ||
602 | return NULL; | ||
603 | } | ||
604 | devinfo_table = list_entry(dl->top, | ||
605 | struct scsi_dev_info_list_table, | ||
606 | node); | ||
607 | dl->bottom = devinfo_table->scsi_dev_info_list.next; | ||
608 | } | ||
609 | |||
610 | return dl; | ||
471 | } | 611 | } |
472 | 612 | ||
473 | static void devinfo_seq_stop(struct seq_file *m, void *v) | 613 | static void devinfo_seq_stop(struct seq_file *m, void *v) |
474 | { | 614 | { |
615 | kfree(v); | ||
475 | } | 616 | } |
476 | 617 | ||
477 | static const struct seq_operations scsi_devinfo_seq_ops = { | 618 | static const struct seq_operations scsi_devinfo_seq_ops = { |
@@ -548,19 +689,78 @@ MODULE_PARM_DESC(default_dev_flags, | |||
548 | **/ | 689 | **/ |
549 | void scsi_exit_devinfo(void) | 690 | void scsi_exit_devinfo(void) |
550 | { | 691 | { |
551 | struct list_head *lh, *lh_next; | ||
552 | struct scsi_dev_info_list *devinfo; | ||
553 | |||
554 | #ifdef CONFIG_SCSI_PROC_FS | 692 | #ifdef CONFIG_SCSI_PROC_FS |
555 | remove_proc_entry("scsi/device_info", NULL); | 693 | remove_proc_entry("scsi/device_info", NULL); |
556 | #endif | 694 | #endif |
557 | 695 | ||
558 | list_for_each_safe(lh, lh_next, &scsi_dev_info_list) { | 696 | scsi_dev_info_remove_list(SCSI_DEVINFO_GLOBAL); |
697 | } | ||
698 | |||
699 | /** | ||
700 | * scsi_dev_info_add_list - add a new devinfo list | ||
701 | * @key: key of the list to add | ||
702 | * @name: Name of the list to add (for /proc/scsi/device_info) | ||
703 | * | ||
704 | * Adds the requested list, returns zero on success, -EEXIST if the | ||
705 | * key is already registered to a list, or other error on failure. | ||
706 | */ | ||
707 | int scsi_dev_info_add_list(int key, const char *name) | ||
708 | { | ||
709 | struct scsi_dev_info_list_table *devinfo_table = | ||
710 | scsi_devinfo_lookup_by_key(key); | ||
711 | |||
712 | if (!IS_ERR(devinfo_table)) | ||
713 | /* list already exists */ | ||
714 | return -EEXIST; | ||
715 | |||
716 | devinfo_table = kmalloc(sizeof(*devinfo_table), GFP_KERNEL); | ||
717 | |||
718 | if (!devinfo_table) | ||
719 | return -ENOMEM; | ||
720 | |||
721 | INIT_LIST_HEAD(&devinfo_table->node); | ||
722 | INIT_LIST_HEAD(&devinfo_table->scsi_dev_info_list); | ||
723 | devinfo_table->name = name; | ||
724 | devinfo_table->key = key; | ||
725 | list_add_tail(&devinfo_table->node, &scsi_dev_info_list); | ||
726 | |||
727 | return 0; | ||
728 | } | ||
729 | EXPORT_SYMBOL(scsi_dev_info_add_list); | ||
730 | |||
731 | /** | ||
732 | * scsi_dev_info_remove_list - destroy an added devinfo list | ||
733 | * @key: key of the list to destroy | ||
734 | * | ||
735 | * Iterates over the entire list first, freeing all the values, then | ||
736 | * frees the list itself. Returns 0 on success or -EINVAL if the key | ||
737 | * can't be found. | ||
738 | */ | ||
739 | int scsi_dev_info_remove_list(int key) | ||
740 | { | ||
741 | struct list_head *lh, *lh_next; | ||
742 | struct scsi_dev_info_list_table *devinfo_table = | ||
743 | scsi_devinfo_lookup_by_key(key); | ||
744 | |||
745 | if (IS_ERR(devinfo_table)) | ||
746 | /* no such list */ | ||
747 | return -EINVAL; | ||
748 | |||
749 | /* remove from the master list */ | ||
750 | list_del(&devinfo_table->node); | ||
751 | |||
752 | list_for_each_safe(lh, lh_next, &devinfo_table->scsi_dev_info_list) { | ||
753 | struct scsi_dev_info_list *devinfo; | ||
754 | |||
559 | devinfo = list_entry(lh, struct scsi_dev_info_list, | 755 | devinfo = list_entry(lh, struct scsi_dev_info_list, |
560 | dev_info_list); | 756 | dev_info_list); |
561 | kfree(devinfo); | 757 | kfree(devinfo); |
562 | } | 758 | } |
759 | kfree(devinfo_table); | ||
760 | |||
761 | return 0; | ||
563 | } | 762 | } |
763 | EXPORT_SYMBOL(scsi_dev_info_remove_list); | ||
564 | 764 | ||
565 | /** | 765 | /** |
566 | * scsi_init_devinfo - set up the dynamic device list. | 766 | * scsi_init_devinfo - set up the dynamic device list. |
@@ -576,10 +776,14 @@ int __init scsi_init_devinfo(void) | |||
576 | #endif | 776 | #endif |
577 | int error, i; | 777 | int error, i; |
578 | 778 | ||
579 | error = scsi_dev_info_list_add_str(scsi_dev_flags); | 779 | error = scsi_dev_info_add_list(SCSI_DEVINFO_GLOBAL, NULL); |
580 | if (error) | 780 | if (error) |
581 | return error; | 781 | return error; |
582 | 782 | ||
783 | error = scsi_dev_info_list_add_str(scsi_dev_flags); | ||
784 | if (error) | ||
785 | goto out; | ||
786 | |||
583 | for (i = 0; scsi_static_device_list[i].vendor; i++) { | 787 | for (i = 0; scsi_static_device_list[i].vendor; i++) { |
584 | error = scsi_dev_info_list_add(1 /* compatibile */, | 788 | error = scsi_dev_info_list_add(1 /* compatibile */, |
585 | scsi_static_device_list[i].vendor, | 789 | scsi_static_device_list[i].vendor, |