From cc747b68f5a124cd641b449e2cf6e7aebb686c9b Mon Sep 17 00:00:00 2001
From: Lee Duncan <lduncan@suse.com>
Date: Wed, 28 Sep 2022 09:19:16 -0700
Subject: [PATCH] scsi: core: Add BLIST_NO_ASK_VPD_SIZE for some VDASD
References: bsc#1203039
Patch-mainline: submitted https://www.spinics.net/lists/linux-scsi/msg177618.html

Some storage, such as AIX VDASD (virtual storage) and IBM 2076
(front end) do not like the recent commit:

commit c92a6b5d6335 ("scsi: core: Query VPD size before getting full page")

That commit changed getting SCSI VPD pages so that we now read
just enough of the page to get the actual page size, then read
the whole page in a second read. The problem is that the above
mentioned hardware returns zero for the page size, because of
a firmware error. In such cases, until the firmware is fixed,
this new black flag says to revert to the original method of
reading the VPD pages, i.e. try to read as a whole buffer's
worth on the first try.

[lduncan: refreshed to apply. Will need kABI mitigation.]

Fixes: c92a6b5d6335 ("scsi: core: Query VPD size before getting full page")
Reported-by: Martin Wilck <mwilck@suse.com>
Suggested-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Lee Duncan <lduncan@suse.com>
---
 drivers/scsi/scsi.c         |   14 +++++++++++---
 drivers/scsi/scsi_devinfo.c |    3 ++-
 drivers/scsi/scsi_scan.c    |    3 +++
 include/scsi/scsi_device.h  |    2 ++
 include/scsi/scsi_devinfo.h |    4 ++--
 5 files changed, 20 insertions(+), 6 deletions(-)

--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -330,12 +330,20 @@ static int scsi_vpd_inquiry(struct scsi_
 	return get_unaligned_be16(&buffer[2]) + 4;
 }
 
-static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page)
+static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page, int buf_len)
 {
 	unsigned char vpd_header[SCSI_VPD_HEADER_SIZE] __aligned(4);
 	int result;
 
 	/*
+	 * if this hardware is blacklisted then don't bother asking
+	 * the page size, since it will repy with zero -- just assume it
+	 * is the buffer size
+	 */
+	if (sdev->no_ask_vpd_sz_first)
+		return buf_len;
+
+	/*
 	 * Fetch the VPD page header to find out how big the page
 	 * is. This is done to prevent problems on legacy devices
 	 * which can not handle allocation lengths as large as
@@ -376,7 +384,7 @@ int scsi_get_vpd_page(struct scsi_device
 	if (!scsi_device_supports_vpd(sdev))
 		return -EINVAL;
 
-	vpd_len = scsi_get_vpd_size(sdev, page);
+	vpd_len = scsi_get_vpd_size(sdev, page, buf_len);
 	if (vpd_len <= 0)
 		return -EINVAL;
 
@@ -411,7 +419,7 @@ static struct scsi_vpd *scsi_get_vpd_buf
 	struct scsi_vpd *vpd_buf;
 	int vpd_len, result;
 
-	vpd_len = scsi_get_vpd_size(sdev, page);
+	vpd_len = scsi_get_vpd_size(sdev, page, SCSI_VPD_PG_LEN);
 	if (vpd_len <= 0)
 		return NULL;
 
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -134,7 +134,7 @@ static struct {
 	{"3PARdata", "VV", NULL, BLIST_REPORTLUN2},
 	{"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN},
 	{"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN},
-	{"AIX", "VDASD", NULL, BLIST_TRY_VPD_PAGES},
+	{"AIX", "VDASD", NULL, BLIST_TRY_VPD_PAGES | BLIST_NO_ASK_VPD_SIZE},
 	{"AFT PRO", "-IX CF", "0.0>", BLIST_FORCELUN},
 	{"BELKIN", "USB 2 HS-CF", "1.95",  BLIST_FORCELUN | BLIST_INQUIRY_36},
 	{"BROWNIE", "1200U3P", NULL, BLIST_NOREPORTLUN},
@@ -187,6 +187,7 @@ static struct {
 	{"HPE", "OPEN-", "*", BLIST_REPORTLUN2 | BLIST_TRY_VPD_PAGES},
 	{"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN},
 	{"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+	{"IBM", "2076", NULL, BLIST_NO_ASK_VPD_SIZE},
 	{"IBM", "2105", NULL, BLIST_RETRY_HWERROR},
 	{"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
 	{"IOMEGA", "ZIP", NULL, BLIST_NOTQ | BLIST_NOLUN},
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -981,6 +981,9 @@ static int scsi_add_lun(struct scsi_devi
 	else if (*bflags & BLIST_SKIP_VPD_PAGES)
 		sdev->skip_vpd_pages = 1;
 
+	if (*bflags & BLIST_NO_ASK_VPD_SIZE)
+		sdev->no_ask_vpd_sz_first = 1;
+
 	transport_configure_device(&sdev->sdev_gendev);
 
 	if (sdev->host->hostt->slave_configure) {
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -144,6 +144,7 @@ struct scsi_device {
 	const char * model;		/* ... after scan; point to static string */
 	const char * rev;		/* ... "nullnullnullnull" before scan */
 
+#define SCSI_VPD_PG_LEN	255	/* default SCSI VPD page size (max) */
 	struct scsi_vpd __rcu *vpd_pg0;
 	struct scsi_vpd __rcu *vpd_pg83;
 	struct scsi_vpd __rcu *vpd_pg80;
@@ -209,6 +210,7 @@ struct scsi_device {
 	unsigned rpm_autosuspend:1;	/* Enable runtime autosuspend at device
 					 * creation time */
 	unsigned silence_suspend:1;	/* Do not print runtime PM related messages */
+	unsigned no_ask_vpd_sz_first:1;	/* Do not ask for VPD size first */
 
 	unsigned int queue_stopped;	/* request queue is quiesced */
 	bool offline_already;		/* Device offline message logged */
--- a/include/scsi/scsi_devinfo.h
+++ b/include/scsi/scsi_devinfo.h
@@ -31,7 +31,8 @@
 #define __BLIST_UNUSED_11	((__force blist_flags_t)(1ULL << 11))
 /* do not do automatic start on add */
 #define BLIST_NOSTARTONADD	((__force blist_flags_t)(1ULL << 12))
-#define __BLIST_UNUSED_13	((__force blist_flags_t)(1ULL << 13))
+/* do not ask for VPD page size first on some broken targets */
+#define BLIST_NO_ASK_VPD_SIZE	((__force blist_flags_t)(1ULL << 13))
 #define __BLIST_UNUSED_14	((__force blist_flags_t)(1ULL << 14))
 #define __BLIST_UNUSED_15	((__force blist_flags_t)(1ULL << 15))
 #define __BLIST_UNUSED_16	((__force blist_flags_t)(1ULL << 16))
@@ -74,7 +75,6 @@
 			       (__force blist_flags_t) \
 			       ((__force __u64)__BLIST_LAST_USED - 1ULL)))
 #define __BLIST_UNUSED_MASK (__BLIST_UNUSED_11 | \
-			     __BLIST_UNUSED_13 | \
 			     __BLIST_UNUSED_14 | \
 			     __BLIST_UNUSED_15 | \
 			     __BLIST_UNUSED_16 | \
