summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi.c
diff options
context:
space:
mode:
authorBart Van Assche <bart.vanassche@wdc.com>2017-08-29 11:50:13 -0400
committerMartin K. Petersen <martin.petersen@oracle.com>2017-08-29 21:51:42 -0400
commitccf1e0045eea8f98d60fc9327bcb14c958d2e4c7 (patch)
treebc32bc41b24ce324d06deec7528c3310db49ddcd /drivers/scsi/scsi.c
parent1e3f720a67c29e145321ed9b4ef7a83e6416d201 (diff)
scsi: Rework handling of scsi_device.vpd_pg8[03]
Introduce struct scsi_vpd for the VPD page length, data and the RCU head that will be used to free the VPD data. Use kfree_rcu() instead of kfree() to free VPD data. Move the VPD buffer pointer check inside the RCU read lock in the sysfs code. Only annotate pointers that are shared across threads with __rcu. Use rcu_dereference() when dereferencing an RCU pointer. This patch suppresses about twenty sparse complaints about the vpd_pg8[03] pointers. This patch also fixes a race condition, namely that updating of the VPD pointers and length variables in struct scsi_device was not atomic with reference to the code reading these variables. See also "Does the update code tolerate concurrent accesses?" in Documentation/RCU/checklist.txt. Fixes: commit 09e2b0b14690 ("scsi: rescan VPD attributes") Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com> Acked-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Shane Seymour <shane.seymour@hpe.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Johannes Thumshirn <jthumshirn@suse.de> Cc: Shane Seymour <shane.seymour@hpe.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r--drivers/scsi/scsi.c44
1 files changed, 18 insertions, 26 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index f3f4926a3e77..d201ebcf4544 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -415,22 +415,20 @@ EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
415 * scsi_get_vpd_buf - Get Vital Product Data from a SCSI device 415 * scsi_get_vpd_buf - Get Vital Product Data from a SCSI device
416 * @sdev: The device to ask 416 * @sdev: The device to ask
417 * @page: Which Vital Product Data to return 417 * @page: Which Vital Product Data to return
418 * @len: Upon success, the VPD length will be stored in *@len.
419 * 418 *
420 * Returns %NULL upon failure. 419 * Returns %NULL upon failure.
421 */ 420 */
422static unsigned char *scsi_get_vpd_buf(struct scsi_device *sdev, u8 page, 421static struct scsi_vpd *scsi_get_vpd_buf(struct scsi_device *sdev, u8 page)
423 int *len)
424{ 422{
425 unsigned char *vpd_buf; 423 struct scsi_vpd *vpd_buf;
426 int vpd_len = SCSI_VPD_PG_LEN, result; 424 int vpd_len = SCSI_VPD_PG_LEN, result;
427 425
428retry_pg: 426retry_pg:
429 vpd_buf = kmalloc(vpd_len, GFP_KERNEL); 427 vpd_buf = kmalloc(sizeof(*vpd_buf) + vpd_len, GFP_KERNEL);
430 if (!vpd_buf) 428 if (!vpd_buf)
431 return NULL; 429 return NULL;
432 430
433 result = scsi_vpd_inquiry(sdev, vpd_buf, page, vpd_len); 431 result = scsi_vpd_inquiry(sdev, vpd_buf->data, page, vpd_len);
434 if (result < 0) { 432 if (result < 0) {
435 kfree(vpd_buf); 433 kfree(vpd_buf);
436 return NULL; 434 return NULL;
@@ -441,31 +439,27 @@ retry_pg:
441 goto retry_pg; 439 goto retry_pg;
442 } 440 }
443 441
444 *len = result; 442 vpd_buf->len = result;
445 443
446 return vpd_buf; 444 return vpd_buf;
447} 445}
448 446
449static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page, 447static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page,
450 unsigned char __rcu **sdev_vpd_buf, 448 struct scsi_vpd __rcu **sdev_vpd_buf)
451 int *sdev_vpd_len)
452{ 449{
453 unsigned char *vpd_buf; 450 struct scsi_vpd *vpd_buf;
454 int len;
455 451
456 vpd_buf = scsi_get_vpd_buf(sdev, page, &len); 452 vpd_buf = scsi_get_vpd_buf(sdev, page);
457 if (!vpd_buf) 453 if (!vpd_buf)
458 return; 454 return;
459 455
460 mutex_lock(&sdev->inquiry_mutex); 456 mutex_lock(&sdev->inquiry_mutex);
461 rcu_swap_protected(*sdev_vpd_buf, vpd_buf, 457 rcu_swap_protected(*sdev_vpd_buf, vpd_buf,
462 lockdep_is_held(&sdev->inquiry_mutex)); 458 lockdep_is_held(&sdev->inquiry_mutex));
463 *sdev_vpd_len = len;
464 mutex_unlock(&sdev->inquiry_mutex); 459 mutex_unlock(&sdev->inquiry_mutex);
465 460
466 synchronize_rcu(); 461 if (vpd_buf)
467 462 kfree_rcu(vpd_buf, rcu);
468 kfree(vpd_buf);
469} 463}
470 464
471/** 465/**
@@ -479,24 +473,22 @@ static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page,
479 */ 473 */
480void scsi_attach_vpd(struct scsi_device *sdev) 474void scsi_attach_vpd(struct scsi_device *sdev)
481{ 475{
482 int i, vpd_len; 476 int i;
483 unsigned char *vpd_buf; 477 struct scsi_vpd *vpd_buf;
484 478
485 if (!scsi_device_supports_vpd(sdev)) 479 if (!scsi_device_supports_vpd(sdev))
486 return; 480 return;
487 481
488 /* Ask for all the pages supported by this device */ 482 /* Ask for all the pages supported by this device */
489 vpd_buf = scsi_get_vpd_buf(sdev, 0, &vpd_len); 483 vpd_buf = scsi_get_vpd_buf(sdev, 0);
490 if (!vpd_buf) 484 if (!vpd_buf)
491 return; 485 return;
492 486
493 for (i = 4; i < vpd_len; i++) { 487 for (i = 4; i < vpd_buf->len; i++) {
494 if (vpd_buf[i] == 0x80) 488 if (vpd_buf->data[i] == 0x80)
495 scsi_update_vpd_page(sdev, 0x80, &sdev->vpd_pg80, 489 scsi_update_vpd_page(sdev, 0x80, &sdev->vpd_pg80);
496 &sdev->vpd_pg80_len); 490 if (vpd_buf->data[i] == 0x83)
497 if (vpd_buf[i] == 0x83) 491 scsi_update_vpd_page(sdev, 0x83, &sdev->vpd_pg83);
498 scsi_update_vpd_page(sdev, 0x83, &sdev->vpd_pg83,
499 &sdev->vpd_pg83_len);
500 } 492 }
501 kfree(vpd_buf); 493 kfree(vpd_buf);
502} 494}