aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_devinfo.c
diff options
context:
space:
mode:
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,