cam/scsi: Support well known logical unit

This patch adds an additional state to probe well-known logical units
before probing normal logical units.

Reviewed by:		imp (mentor)
Sponsored by:		Samsung Electronics
Differential Revision:	https://reviews.freebsd.org/D53920
This commit is contained in:
Jaeyoon Choi
2025-12-01 04:38:52 +00:00
parent 3e3d7e5905
commit ba96377619
5 changed files with 161 additions and 83 deletions
+1
View File
@@ -614,6 +614,7 @@ typedef enum {
} pi_tmflag; } pi_tmflag;
typedef enum { typedef enum {
PIM_WLUNS = 0x400,/* Well known LUNs supported */
PIM_ATA_EXT = 0x200,/* ATA requests can understand ata_ext requests */ PIM_ATA_EXT = 0x200,/* ATA requests can understand ata_ext requests */
PIM_EXTLUNS = 0x100,/* 64bit extended LUNs supported */ PIM_EXTLUNS = 0x100,/* 64bit extended LUNs supported */
PIM_SCANHILO = 0x80, /* Bus scans from high ID to low ID */ PIM_SCANHILO = 0x80, /* Bus scans from high ID to low ID */
+1
View File
@@ -4682,6 +4682,7 @@ xpt_alloc_target(struct cam_eb *bus, target_id_t target_id)
target->refcount = 1; target->refcount = 1;
target->generation = 0; target->generation = 0;
target->luns = NULL; target->luns = NULL;
target->wluns = NULL;
mtx_init(&target->luns_mtx, "CAM LUNs lock", NULL, MTX_DEF); mtx_init(&target->luns_mtx, "CAM LUNs lock", NULL, MTX_DEF);
timevalclear(&target->last_reset); timevalclear(&target->last_reset);
/* /*
+1
View File
@@ -169,6 +169,7 @@ struct cam_et {
struct timeval last_reset; struct timeval last_reset;
u_int rpl_size; u_int rpl_size;
struct scsi_report_luns_data *luns; struct scsi_report_luns_data *luns;
struct scsi_report_luns_data *wluns;
struct mtx luns_mtx; /* Protection for luns field. */ struct mtx luns_mtx; /* Protection for luns field. */
}; };
+1 -1
View File
@@ -3057,7 +3057,7 @@ struct scsi_report_luns_data {
uint8_t length[4]; /* length of LUN inventory, in bytes */ uint8_t length[4]; /* length of LUN inventory, in bytes */
uint8_t reserved[4]; /* unused */ uint8_t reserved[4]; /* unused */
/* /*
* LUN inventory- we only support the type zero form for now. * LUN inventory- we only support type zero and extended (well-known) formats.
*/ */
struct scsi_report_luns_lundata luns[0]; struct scsi_report_luns_lundata luns[0];
}; };
+96 -21
View File
@@ -130,6 +130,7 @@ typedef enum {
PROBE_TUR, PROBE_TUR,
PROBE_INQUIRY, /* this counts as DV0 for Basic Domain Validation */ PROBE_INQUIRY, /* this counts as DV0 for Basic Domain Validation */
PROBE_FULL_INQUIRY, PROBE_FULL_INQUIRY,
PROBE_REPORT_WLUNS,
PROBE_REPORT_LUNS, PROBE_REPORT_LUNS,
PROBE_MODE_SENSE, PROBE_MODE_SENSE,
PROBE_SUPPORTED_VPD_LIST, PROBE_SUPPORTED_VPD_LIST,
@@ -148,6 +149,7 @@ static char *probe_action_text[] = {
"PROBE_TUR", "PROBE_TUR",
"PROBE_INQUIRY", "PROBE_INQUIRY",
"PROBE_FULL_INQUIRY", "PROBE_FULL_INQUIRY",
"PROBE_REPORT_WLUNS",
"PROBE_REPORT_LUNS", "PROBE_REPORT_LUNS",
"PROBE_MODE_SENSE", "PROBE_MODE_SENSE",
"PROBE_SUPPORTED_VPD_LIST", "PROBE_SUPPORTED_VPD_LIST",
@@ -567,7 +569,7 @@ static int proberequestbackoff(struct cam_periph *periph,
static void probedone(struct cam_periph *periph, union ccb *done_ccb); static void probedone(struct cam_periph *periph, union ccb *done_ccb);
static void probe_purge_old(struct cam_path *path, static void probe_purge_old(struct cam_path *path,
struct scsi_report_luns_data *new, struct scsi_report_luns_data *new,
probe_flags flags); probe_flags flags, bool is_wlun);
static void probecleanup(struct cam_periph *periph); static void probecleanup(struct cam_periph *periph);
static void scsi_find_quirk(struct cam_ed *device); static void scsi_find_quirk(struct cam_ed *device);
static void scsi_scan_bus(struct cam_periph *periph, union ccb *ccb); static void scsi_scan_bus(struct cam_periph *periph, union ccb *ccb);
@@ -817,6 +819,23 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
/*timeout*/60 * 1000); /*timeout*/60 * 1000);
break; break;
} }
case PROBE_REPORT_WLUNS:
{
void *rp;
rp = malloc(periph->path->target->rpl_size,
M_CAMXPT, M_NOWAIT | M_ZERO);
if (rp == NULL) {
xpt_print(periph->path,
"Unable to alloc report wluns storage\n");
PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS);
goto again;
}
scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
RPL_REPORT_WELLKNOWN, rp, periph->path->target->rpl_size,
SSD_FULL_SIZE, 60000);
break;
}
case PROBE_REPORT_LUNS: case PROBE_REPORT_LUNS:
{ {
void *rp; void *rp;
@@ -1162,6 +1181,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
struct cam_path *path; struct cam_path *path;
struct scsi_inquiry_data *inq_buf; struct scsi_inquiry_data *inq_buf;
uint32_t priority; uint32_t priority;
struct ccb_pathinq cpi;
CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n"));
@@ -1169,6 +1189,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
path = done_ccb->ccb_h.path; path = done_ccb->ccb_h.path;
priority = done_ccb->ccb_h.pinfo.priority; priority = done_ccb->ccb_h.pinfo.priority;
cam_periph_assert(periph, MA_OWNED); cam_periph_assert(periph, MA_OWNED);
xpt_path_inq(&cpi, path);
switch (softc->action) { switch (softc->action) {
case PROBE_TUR: case PROBE_TUR:
@@ -1235,8 +1256,10 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
SID_ANSI_REV(inq_buf) > SCSI_REV_SPC2 && SID_ANSI_REV(inq_buf) > SCSI_REV_SPC2 &&
(SCSI_QUIRK(path->device)->quirks & (SCSI_QUIRK(path->device)->quirks &
CAM_QUIRK_NORPTLUNS) == 0) { CAM_QUIRK_NORPTLUNS) == 0) {
PROBE_SET_ACTION(softc, if (cpi.hba_misc & PIM_WLUNS)
PROBE_REPORT_LUNS); PROBE_SET_ACTION(softc, PROBE_REPORT_WLUNS);
else
PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS);
/* /*
* Start with room for *one* lun. * Start with room for *one* lun.
*/ */
@@ -1259,6 +1282,9 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
SID_ANSI_REV(inq_buf) >= SCSI_REV_SPC2 && SID_ANSI_REV(inq_buf) >= SCSI_REV_SPC2 &&
(SCSI_QUIRK(path->device)->quirks & (SCSI_QUIRK(path->device)->quirks &
CAM_QUIRK_NORPTLUNS) == 0) { CAM_QUIRK_NORPTLUNS) == 0) {
if (cpi.hba_misc & PIM_WLUNS)
PROBE_SET_ACTION(softc, PROBE_REPORT_WLUNS);
else
PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS); PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS);
periph->path->target->rpl_size = 16; periph->path->target->rpl_size = 16;
xpt_release_ccb(done_ccb); xpt_release_ccb(done_ccb);
@@ -1296,11 +1322,13 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
xpt_release_ccb(done_ccb); xpt_release_ccb(done_ccb);
break; break;
} }
case PROBE_REPORT_WLUNS:
case PROBE_REPORT_LUNS: case PROBE_REPORT_LUNS:
{ {
struct ccb_scsiio *csio; struct ccb_scsiio *csio;
struct scsi_report_luns_data *lp; struct scsi_report_luns_data *lp;
u_int nlun, maxlun; u_int nlun, maxlun;
bool is_wlun = softc->action == PROBE_REPORT_WLUNS;
csio = &done_ccb->csio; csio = &done_ccb->csio;
@@ -1377,7 +1405,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
* This function will also install the new list * This function will also install the new list
* in the target structure. * in the target structure.
*/ */
probe_purge_old(path, lp, softc->flags); probe_purge_old(path, lp, softc->flags, is_wlun);
lp = NULL; lp = NULL;
} }
/* The processing above should either exit via a `goto /* The processing above should either exit via a `goto
@@ -1390,7 +1418,9 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
if (path->device->flags & CAM_DEV_INQUIRY_DATA_VALID && if (path->device->flags & CAM_DEV_INQUIRY_DATA_VALID &&
(SID_QUAL(inq_buf) == SID_QUAL_LU_CONNECTED || (SID_QUAL(inq_buf) == SID_QUAL_LU_CONNECTED ||
SID_QUAL(inq_buf) == SID_QUAL_LU_OFFLINE)) { SID_QUAL(inq_buf) == SID_QUAL_LU_OFFLINE)) {
if (INQ_DATA_TQ_ENABLED(inq_buf)) if (is_wlun)
PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS);
else if (INQ_DATA_TQ_ENABLED(inq_buf))
PROBE_SET_ACTION(softc, PROBE_MODE_SENSE); PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
else else
PROBE_SET_ACTION(softc, PROBE_SET_ACTION(softc,
@@ -1815,20 +1845,22 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
static void static void
probe_purge_old(struct cam_path *path, struct scsi_report_luns_data *new, probe_purge_old(struct cam_path *path, struct scsi_report_luns_data *new,
probe_flags flags) probe_flags flags, bool is_wlun)
{ {
struct cam_path *tp; struct cam_path *tp;
struct scsi_report_luns_data *old; struct scsi_report_luns_data **luns_data, *old;
u_int idx1, idx2, nlun_old, nlun_new; u_int idx1, idx2, nlun_old, nlun_new;
lun_id_t this_lun; lun_id_t this_lun;
uint8_t *ol, *nl; uint8_t *ol, *nl;
luns_data = is_wlun ? &path->target->wluns : &path->target->luns;
if (path->target == NULL) { if (path->target == NULL) {
return; return;
} }
mtx_lock(&path->target->luns_mtx); mtx_lock(&path->target->luns_mtx);
old = path->target->luns; old = *luns_data;
path->target->luns = new; *luns_data = new;
mtx_unlock(&path->target->luns_mtx); mtx_unlock(&path->target->luns_mtx);
if (old == NULL) if (old == NULL)
return; return;
@@ -1908,11 +1940,16 @@ scsi_find_quirk(struct cam_ed *device)
device->maxtags = quirk->maxtags; device->maxtags = quirk->maxtags;
} }
typedef struct {
int lun;
int wlun;
} lun_pair;
typedef struct { typedef struct {
union ccb *request_ccb; union ccb *request_ccb;
struct ccb_pathinq *cpi; struct ccb_pathinq *cpi;
int counter; int counter;
int lunindex[0]; lun_pair lunindex[0];
} scsi_scan_bus_info; } scsi_scan_bus_info;
static void static void
@@ -1995,7 +2032,8 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
/* Save some state for use while we probe for devices */ /* Save some state for use while we probe for devices */
scan_info = (scsi_scan_bus_info *) malloc(sizeof(scsi_scan_bus_info) + scan_info = (scsi_scan_bus_info *) malloc(sizeof(scsi_scan_bus_info) +
(work_ccb->cpi.max_target * sizeof (u_int)), M_CAMXPT, M_ZERO|M_NOWAIT); (work_ccb->cpi.max_target * sizeof(lun_pair)),
M_CAMXPT, M_ZERO|M_NOWAIT);
if (scan_info == NULL) { if (scan_info == NULL) {
request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_free_ccb(work_ccb); xpt_free_ccb(work_ccb);
@@ -2080,6 +2118,8 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
path_id_t path_id; path_id_t path_id;
target_id_t target_id; target_id_t target_id;
lun_id_t lun_id; lun_id_t lun_id;
u_int nwluns;
bool need_wlun_scan = false;
oldpath = request_ccb->ccb_h.path; oldpath = request_ccb->ccb_h.path;
@@ -2093,6 +2133,40 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path); mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path);
mtx_lock(mtx); mtx_lock(mtx);
if (scan_info->cpi->hba_misc & PIM_WLUNS) {
/* Scan Well known logical units */
mtx_lock(&target->luns_mtx);
if (target->wluns) {
nwluns = scsi_4btoul(target->wluns->length) / 8;
if (scan_info->lunindex[target_id].wlun < nwluns)
need_wlun_scan = true;
}
if (need_wlun_scan) {
/*
* WLUN uses the Extended WLUN address format, so we can handle all of
* them.
*/
CAM_GET_LUN(target->wluns, scan_info->lunindex[target_id].wlun, lun_id);
mtx_unlock(&target->luns_mtx);
next_target = 0;
CAM_DEBUG(request_ccb->ccb_h.path,
CAM_DEBUG_PROBE,
("next wlun to try at index %u is %jx\n",
scan_info->lunindex[target_id].wlun,
(uintmax_t)lun_id));
scan_info->lunindex[target_id].wlun++;
} else {
mtx_unlock(&target->luns_mtx);
/* We're done with scanning all wluns. */
}
}
if (!need_wlun_scan) {
/* Scan logical units */
mtx_lock(&target->luns_mtx); mtx_lock(&target->luns_mtx);
if (target->luns) { if (target->luns) {
lun_id_t first; lun_id_t first;
@@ -2104,41 +2178,41 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
* it. * it.
*/ */
CAM_GET_LUN(target->luns, 0, first); CAM_GET_LUN(target->luns, 0, first);
if (first == 0 && scan_info->lunindex[target_id] == 0) { if (first == 0 && scan_info->lunindex[target_id].lun == 0) {
scan_info->lunindex[target_id]++; scan_info->lunindex[target_id].lun++;
} }
/* /*
* Skip any LUNs that the HBA can't deal with. * Skip any LUNs that the HBA can't deal with.
*/ */
while (scan_info->lunindex[target_id] < nluns) { while (scan_info->lunindex[target_id].lun < nluns) {
if (scan_info->cpi->hba_misc & PIM_EXTLUNS) { if (scan_info->cpi->hba_misc & PIM_EXTLUNS) {
CAM_GET_LUN(target->luns, CAM_GET_LUN(target->luns,
scan_info->lunindex[target_id], scan_info->lunindex[target_id].lun,
lun_id); lun_id);
break; break;
} }
if (CAM_CAN_GET_SIMPLE_LUN(target->luns, if (CAM_CAN_GET_SIMPLE_LUN(target->luns,
scan_info->lunindex[target_id])) { scan_info->lunindex[target_id].lun)) {
CAM_GET_SIMPLE_LUN(target->luns, CAM_GET_SIMPLE_LUN(target->luns,
scan_info->lunindex[target_id], scan_info->lunindex[target_id].lun,
lun_id); lun_id);
break; break;
} }
scan_info->lunindex[target_id]++; scan_info->lunindex[target_id].lun++;
} }
if (scan_info->lunindex[target_id] < nluns) { if (scan_info->lunindex[target_id].lun < nluns) {
mtx_unlock(&target->luns_mtx); mtx_unlock(&target->luns_mtx);
next_target = 0; next_target = 0;
CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG(request_ccb->ccb_h.path,
CAM_DEBUG_PROBE, CAM_DEBUG_PROBE,
("next lun to try at index %u is %jx\n", ("next lun to try at index %u is %jx\n",
scan_info->lunindex[target_id], scan_info->lunindex[target_id].lun,
(uintmax_t)lun_id)); (uintmax_t)lun_id));
scan_info->lunindex[target_id]++; scan_info->lunindex[target_id].lun++;
} else { } else {
mtx_unlock(&target->luns_mtx); mtx_unlock(&target->luns_mtx);
/* We're done with scanning all luns. */ /* We're done with scanning all luns. */
@@ -2178,6 +2252,7 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
next_target = 1; next_target = 1;
} }
} }
}
/* /*
* Check to see if we scan any further luns. * Check to see if we scan any further luns.