aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_devinfo.c
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2009-06-17 15:01:58 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-06-21 11:52:45 -0400
commit598fa4b775d064d4656132c78d9a312eb1d2f91f (patch)
tree42383f579d64e1eaf4a3cfa5b284699deb050839 /drivers/scsi/scsi_devinfo.c
parente0420029dedb867e5c2c044a0737338a7e9cec0b (diff)
enhance device info matching for multiple tables
The current scsi_devinfo.c matching routines use a single table for the global blacklist. However, we're developing a need to blacklist from specific transports too (notably some tape drives using SPI which don't respond well to high speed protocols). Instead of developing separate blacklist matching for each transport class needing it, enhance the current list matching to permit multiple lists. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/scsi_devinfo.c')
-rw-r--r--drivers/scsi/scsi_devinfo.c247
1 files changed, 225 insertions, 22 deletions
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 8821df9a277b..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
27struct 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
28static const char spaces[] = " "; /* 16 of them */ 35static const char spaces[] = " "; /* 16 of them */
29static unsigned scsi_default_dev_flags; 36static unsigned scsi_default_dev_flags;
@@ -247,6 +254,22 @@ static struct {
247 { NULL, NULL, NULL, 0 }, 254 { NULL, NULL, NULL, 0 },
248}; 255};
249 256
257static 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
250/* 273/*
251 * 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
252 * devinfo vendor and model strings. 275 * devinfo vendor and model strings.
@@ -296,7 +319,38 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length,
296static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, 319static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
297 char *strflags, int flags) 320 char *strflags, int flags)
298{ 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 **/
345int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model,
346 char *strflags, int flags, int key)
347{
299 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);
300 354
301 devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); 355 devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL);
302 if (!devinfo) { 356 if (!devinfo) {
@@ -317,12 +371,15 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
317 devinfo->compatible = compatible; 371 devinfo->compatible = compatible;
318 372
319 if (compatible) 373 if (compatible)
320 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);
321 else 376 else
322 list_add(&devinfo->dev_info_list, &scsi_dev_info_list); 377 list_add(&devinfo->dev_info_list,
378 &devinfo_table->scsi_dev_info_list);
323 379
324 return 0; 380 return 0;
325} 381}
382EXPORT_SYMBOL(scsi_dev_info_list_add_keyed);
326 383
327/** 384/**
328 * 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.
@@ -382,22 +439,48 @@ static int scsi_dev_info_list_add_str(char *dev_list)
382 * @model: model name 439 * @model: model name
383 * 440 *
384 * Description: 441 * Description:
385 * Search the scsi_dev_info_list for an entry matching @vendor and 442 * Search the global scsi_dev_info_list (specified by list zero)
386 * @model, if found, return the matching flags value, else return 443 * for an entry matching @vendor and @model, if found, return the
387 * 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.
388 **/ 446 **/
389int scsi_get_device_flags(struct scsi_device *sdev, 447int scsi_get_device_flags(struct scsi_device *sdev,
390 const unsigned char *vendor, 448 const unsigned char *vendor,
391 const unsigned char *model) 449 const unsigned char *model)
392{ 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 **/
469int scsi_get_device_flags_keyed(struct scsi_device *sdev,
470 const unsigned char *vendor,
471 const unsigned char *model,
472 int key)
473{
393 struct scsi_dev_info_list *devinfo; 474 struct scsi_dev_info_list *devinfo;
394 unsigned int bflags; 475 struct scsi_dev_info_list_table *devinfo_table;
476
477 devinfo_table = scsi_devinfo_lookup_by_key(key);
395 478
396 bflags = sdev->sdev_bflags; 479 if (IS_ERR(devinfo_table))
397 if (!bflags) 480 return PTR_ERR(devinfo_table);
398 bflags = scsi_default_dev_flags;
399 481
400 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) {
401 if (devinfo->compatible) { 484 if (devinfo->compatible) {
402 /* 485 /*
403 * Behave like the older version of get_device_flags. 486 * Behave like the older version of get_device_flags.
@@ -447,32 +530,89 @@ int scsi_get_device_flags(struct scsi_device *sdev,
447 return devinfo->flags; 530 return devinfo->flags;
448 } 531 }
449 } 532 }
450 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;
451} 542}
543EXPORT_SYMBOL(scsi_get_device_flags_keyed);
452 544
453#ifdef CONFIG_SCSI_PROC_FS 545#ifdef CONFIG_SCSI_PROC_FS
546struct double_list {
547 struct list_head *top;
548 struct list_head *bottom;
549};
550
454static int devinfo_seq_show(struct seq_file *m, void *v) 551static int devinfo_seq_show(struct seq_file *m, void *v)
455{ 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);
456 struct scsi_dev_info_list *devinfo = 556 struct scsi_dev_info_list *devinfo =
457 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);
458 563
459 seq_printf(m, "'%.8s' '%.16s' 0x%x\n", 564 seq_printf(m, "'%.8s' '%.16s' 0x%x\n",
460 devinfo->vendor, devinfo->model, devinfo->flags); 565 devinfo->vendor, devinfo->model, devinfo->flags);
461 return 0; 566 return 0;
462} 567}
463 568
464static void * devinfo_seq_start(struct seq_file *m, loff_t *pos) 569static void *devinfo_seq_start(struct seq_file *m, loff_t *ppos)
465{ 570{
466 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;
467} 588}
468 589
469static void * devinfo_seq_next(struct seq_file *m, void *v, loff_t *pos) 590static void *devinfo_seq_next(struct seq_file *m, void *v, loff_t *ppos)
470{ 591{
471 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;
472} 611}
473 612
474static void devinfo_seq_stop(struct seq_file *m, void *v) 613static void devinfo_seq_stop(struct seq_file *m, void *v)
475{ 614{
615 kfree(v);
476} 616}
477 617
478static const struct seq_operations scsi_devinfo_seq_ops = { 618static const struct seq_operations scsi_devinfo_seq_ops = {
@@ -549,19 +689,78 @@ MODULE_PARM_DESC(default_dev_flags,
549 **/ 689 **/
550void scsi_exit_devinfo(void) 690void scsi_exit_devinfo(void)
551{ 691{
552 struct list_head *lh, *lh_next;
553 struct scsi_dev_info_list *devinfo;
554
555#ifdef CONFIG_SCSI_PROC_FS 692#ifdef CONFIG_SCSI_PROC_FS
556 remove_proc_entry("scsi/device_info", NULL); 693 remove_proc_entry("scsi/device_info", NULL);
557#endif 694#endif
558 695
559 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 */
707int 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}
729EXPORT_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 */
739int 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
560 devinfo = list_entry(lh, struct scsi_dev_info_list, 755 devinfo = list_entry(lh, struct scsi_dev_info_list,
561 dev_info_list); 756 dev_info_list);
562 kfree(devinfo); 757 kfree(devinfo);
563 } 758 }
759 kfree(devinfo_table);
760
761 return 0;
564} 762}
763EXPORT_SYMBOL(scsi_dev_info_remove_list);
565 764
566/** 765/**
567 * scsi_init_devinfo - set up the dynamic device list. 766 * scsi_init_devinfo - set up the dynamic device list.
@@ -577,10 +776,14 @@ int __init scsi_init_devinfo(void)
577#endif 776#endif
578 int error, i; 777 int error, i;
579 778
580 error = scsi_dev_info_list_add_str(scsi_dev_flags); 779 error = scsi_dev_info_add_list(SCSI_DEVINFO_GLOBAL, NULL);
581 if (error) 780 if (error)
582 return error; 781 return error;
583 782
783 error = scsi_dev_info_list_add_str(scsi_dev_flags);
784 if (error)
785 goto out;
786
584 for (i = 0; scsi_static_device_list[i].vendor; i++) { 787 for (i = 0; scsi_static_device_list[i].vendor; i++) {
585 error = scsi_dev_info_list_add(1 /* compatibile */, 788 error = scsi_dev_info_list_add(1 /* compatibile */,
586 scsi_static_device_list[i].vendor, 789 scsi_static_device_list[i].vendor,