Make CAM ATA stack handle disk resizes.

While for ATA disks resize is even more rare situation than for SCSI, it
may happen in case of HPA or AMA being used.  Make ATA XPT report minor
IDENTIFY DATA change to upper layers with AC_GETDEV_CHANGED, and ada(4)
periph driver handle that event, recalculating all the disk properties and
signalling resize to GEOM.  Since ATA has no mechanism of UNIT ATTENTIONs,
like SCSI, it has no way to detect that something has changed.  That is why
this functionality depends on explicit reprobe via XPT_REPROBE_LUN call.

MFC after:	2 weeks
Relnotes:	yes
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2019-07-23 02:11:14 +00:00
parent b75bd60a04
commit 76d843dab2
2 changed files with 112 additions and 96 deletions

View File

@ -248,8 +248,9 @@ struct ada_softc {
int periodic_read_error;
int periodic_read_count;
#endif
struct disk_params params;
struct disk *disk;
struct ccb_pathinq cpi;
struct disk_params params;
struct disk *disk;
struct task sysctl_task;
struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
@ -807,6 +808,8 @@ static void adasysctlinit(void *context, int pending);
static int adagetattr(struct bio *bp);
static void adasetflags(struct ada_softc *softc,
struct ccb_getdev *cgd);
static void adasetgeom(struct ada_softc *softc,
struct ccb_getdev *cgd);
static periph_ctor_t adaregister;
static void ada_dsmtrim(struct ada_softc *softc, struct bio *bp,
struct ccb_ataio *ataio);
@ -822,8 +825,6 @@ static void adadone(struct cam_periph *periph,
union ccb *done_ccb);
static int adaerror(union ccb *ccb, u_int32_t cam_flags,
u_int32_t sense_flags);
static void adagetparams(struct cam_periph *periph,
struct ccb_getdev *cgd);
static timeout_t adasendorderedtag;
static void adashutdown(void *arg, int howto);
static void adasuspend(void *arg);
@ -1307,9 +1308,11 @@ adaasync(void *callback_arg, u_int32_t code,
xpt_action((union ccb *)&cgd);
/*
* Set/clear support flags based on the new Identify data.
* Update our information based on the new Identify data.
*/
adasetflags(softc, &cgd);
adasetgeom(softc, &cgd);
disk_resize(softc->disk, M_NOWAIT);
cam_periph_async(periph, code, path, arg);
break;
@ -1645,7 +1648,8 @@ adasetflags(struct ada_softc *softc, struct ccb_getdev *cgd)
softc->flags &= ~ADA_FLAG_CAN_NCQ;
if ((cgd->ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) &&
(cgd->inq_flags & SID_DMA)) {
(cgd->inq_flags & SID_DMA) &&
(softc->quirks & ADA_Q_NO_TRIM) == 0) {
softc->flags |= ADA_FLAG_CAN_TRIM;
softc->trim_max_ranges = TRIM_MAX_RANGES;
if (cgd->ident_data.max_dsm_blocks != 0) {
@ -1713,13 +1717,11 @@ static cam_status
adaregister(struct cam_periph *periph, void *arg)
{
struct ada_softc *softc;
struct ccb_pathinq cpi;
struct ccb_getdev *cgd;
struct disk_params *dp;
struct sbuf sb;
char *announce_buf;
caddr_t match;
u_int maxio;
int quirks;
cgd = (struct ccb_getdev *)arg;
@ -1748,6 +1750,7 @@ adaregister(struct cam_periph *periph, void *arg)
}
periph->softc = softc;
xpt_path_inq(&softc->cpi, periph->path);
/*
* See if this device has any quirks.
@ -1761,8 +1764,6 @@ adaregister(struct cam_periph *periph, void *arg)
else
softc->quirks = ADA_Q_NONE;
xpt_path_inq(&cpi, periph->path);
TASK_INIT(&softc->sysctl_task, 0, adasysctlinit, periph);
/*
@ -1788,6 +1789,8 @@ adaregister(struct cam_periph *periph, void *arg)
* Set support flags based on the Identify data and quirks.
*/
adasetflags(softc, cgd);
if (softc->cpi.hba_misc & PIM_ATA_EXT)
softc->flags |= ADA_FLAG_PIM_ATA_EXT;
/* Disable queue sorting for non-rotational media by default. */
if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) {
@ -1796,14 +1799,13 @@ adaregister(struct cam_periph *periph, void *arg)
softc->rotating = 1;
}
cam_iosched_set_sort_queue(softc->cam_iosched, softc->rotating ? -1 : 0);
adagetparams(periph, cgd);
softc->disk = disk_alloc();
softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate;
adasetgeom(softc, cgd);
softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
periph->unit_number, softc->params.secsize,
DEVSTAT_ALL_SUPPORTED,
DEVSTAT_TYPE_DIRECT |
XPORT_DEVSTAT_TYPE(cpi.transport),
XPORT_DEVSTAT_TYPE(softc->cpi.transport),
DEVSTAT_PRIORITY_DISK);
softc->disk->d_open = adaopen;
softc->disk->d_close = adaclose;
@ -1813,69 +1815,7 @@ adaregister(struct cam_periph *periph, void *arg)
softc->disk->d_gone = adadiskgonecb;
softc->disk->d_name = "ada";
softc->disk->d_drv1 = periph;
maxio = cpi.maxio; /* Honor max I/O size of SIM */
if (maxio == 0)
maxio = DFLTPHYS; /* traditional default */
else if (maxio > MAXPHYS)
maxio = MAXPHYS; /* for safety */
if (softc->flags & ADA_FLAG_CAN_48BIT)
maxio = min(maxio, 65536 * softc->params.secsize);
else /* 28bit ATA command limit */
maxio = min(maxio, 256 * softc->params.secsize);
if (softc->quirks & ADA_Q_128KB)
maxio = min(maxio, 128 * 1024);
softc->disk->d_maxsize = maxio;
softc->disk->d_unit = periph->unit_number;
softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION | DISKFLAG_CANZONE;
if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
/* Device lies about TRIM capability. */
if ((softc->quirks & ADA_Q_NO_TRIM) &&
(softc->flags & ADA_FLAG_CAN_TRIM))
softc->flags &= ~ADA_FLAG_CAN_TRIM;
if (softc->flags & ADA_FLAG_CAN_TRIM) {
softc->disk->d_flags |= DISKFLAG_CANDELETE;
softc->disk->d_delmaxsize = softc->params.secsize *
ATA_DSM_RANGE_MAX *
softc->trim_max_ranges;
} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
!(softc->flags & ADA_FLAG_CAN_48BIT)) {
softc->disk->d_flags |= DISKFLAG_CANDELETE;
softc->disk->d_delmaxsize = 256 * softc->params.secsize;
} else
softc->disk->d_delmaxsize = maxio;
if ((cpi.hba_misc & PIM_UNMAPPED) != 0) {
softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
softc->unmappedio = 1;
}
if (cpi.hba_misc & PIM_ATA_EXT)
softc->flags |= ADA_FLAG_PIM_ATA_EXT;
strlcpy(softc->disk->d_descr, cgd->ident_data.model,
MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
softc->disk->d_hba_vendor = cpi.hba_vendor;
softc->disk->d_hba_device = cpi.hba_device;
softc->disk->d_hba_subvendor = cpi.hba_subvendor;
softc->disk->d_hba_subdevice = cpi.hba_subdevice;
softc->disk->d_sectorsize = softc->params.secsize;
softc->disk->d_mediasize = (off_t)softc->params.sectors *
softc->params.secsize;
if (ata_physical_sector_size(&cgd->ident_data) !=
softc->params.secsize) {
softc->disk->d_stripesize =
ata_physical_sector_size(&cgd->ident_data);
softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
ata_logical_sector_offset(&cgd->ident_data)) %
softc->disk->d_stripesize;
} else if (softc->quirks & ADA_Q_4K) {
softc->disk->d_stripesize = 4096;
softc->disk->d_stripeoffset = 0;
}
softc->disk->d_fwsectors = softc->params.secs_per_track;
softc->disk->d_fwheads = softc->params.heads;
ata_disk_firmware_geom_adjust(softc->disk);
/*
* Acquire a reference to the periph before we register with GEOM.
@ -3388,16 +3328,17 @@ adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
}
static void
adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
adasetgeom(struct ada_softc *softc, struct ccb_getdev *cgd)
{
struct ada_softc *softc = (struct ada_softc *)periph->softc;
struct disk_params *dp = &softc->params;
u_int64_t lbasize48;
u_int32_t lbasize;
u_int maxio, d_flags;
dp->secsize = ata_logical_sector_size(&cgd->ident_data);
if ((cgd->ident_data.atavalid & ATA_FLAG_54_58) &&
cgd->ident_data.current_heads && cgd->ident_data.current_sectors) {
cgd->ident_data.current_heads != 0 &&
cgd->ident_data.current_sectors != 0) {
dp->heads = cgd->ident_data.current_heads;
dp->secs_per_track = cgd->ident_data.current_sectors;
dp->cylinders = cgd->ident_data.cylinders;
@ -3425,6 +3366,60 @@ adagetparams(struct cam_periph *periph, struct ccb_getdev *cgd)
if ((cgd->ident_data.support.command2 & ATA_SUPPORT_ADDRESS48) &&
lbasize48 > ATA_MAX_28BIT_LBA)
dp->sectors = lbasize48;
maxio = softc->cpi.maxio; /* Honor max I/O size of SIM */
if (maxio == 0)
maxio = DFLTPHYS; /* traditional default */
else if (maxio > MAXPHYS)
maxio = MAXPHYS; /* for safety */
if (softc->flags & ADA_FLAG_CAN_48BIT)
maxio = min(maxio, 65536 * softc->params.secsize);
else /* 28bit ATA command limit */
maxio = min(maxio, 256 * softc->params.secsize);
if (softc->quirks & ADA_Q_128KB)
maxio = min(maxio, 128 * 1024);
softc->disk->d_maxsize = maxio;
d_flags = DISKFLAG_DIRECT_COMPLETION | DISKFLAG_CANZONE;
if (softc->flags & ADA_FLAG_CAN_FLUSHCACHE)
d_flags |= DISKFLAG_CANFLUSHCACHE;
if (softc->flags & ADA_FLAG_CAN_TRIM) {
d_flags |= DISKFLAG_CANDELETE;
softc->disk->d_delmaxsize = softc->params.secsize *
ATA_DSM_RANGE_MAX * softc->trim_max_ranges;
} else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
!(softc->flags & ADA_FLAG_CAN_48BIT)) {
d_flags |= DISKFLAG_CANDELETE;
softc->disk->d_delmaxsize = 256 * softc->params.secsize;
} else
softc->disk->d_delmaxsize = maxio;
if ((softc->cpi.hba_misc & PIM_UNMAPPED) != 0) {
d_flags |= DISKFLAG_UNMAPPED_BIO;
softc->unmappedio = 1;
}
softc->disk->d_flags = d_flags;
strlcpy(softc->disk->d_descr, cgd->ident_data.model,
MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model)));
strlcpy(softc->disk->d_ident, cgd->ident_data.serial,
MIN(sizeof(softc->disk->d_ident), sizeof(cgd->ident_data.serial)));
softc->disk->d_sectorsize = softc->params.secsize;
softc->disk->d_mediasize = (off_t)softc->params.sectors *
softc->params.secsize;
if (ata_physical_sector_size(&cgd->ident_data) !=
softc->params.secsize) {
softc->disk->d_stripesize =
ata_physical_sector_size(&cgd->ident_data);
softc->disk->d_stripeoffset = (softc->disk->d_stripesize -
ata_logical_sector_offset(&cgd->ident_data)) %
softc->disk->d_stripesize;
} else if (softc->quirks & ADA_Q_4K) {
softc->disk->d_stripesize = 4096;
softc->disk->d_stripeoffset = 0;
}
softc->disk->d_fwsectors = softc->params.secs_per_track;
softc->disk->d_fwheads = softc->params.heads;
ata_disk_firmware_geom_adjust(softc->disk);
softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate;
}
static void

View File

@ -344,6 +344,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
probe_softc *softc;
struct cam_path *path;
struct ata_params *ident_buf;
u_int oif;
CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n"));
@ -383,7 +384,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
/*data_ptr*/(u_int8_t *)&softc->ident_data,
/*dxfer_len*/sizeof(softc->ident_data),
30 * 1000);
if (periph->path->device->protocol == PROTO_ATA)
if (path->device->protocol == PROTO_ATA)
ata_28bit_cmd(ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
else
ata_28bit_cmd(ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0);
@ -419,7 +420,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_MODE)
mode = cts.xport_specific.sata.mode;
}
if (periph->path->device->protocol == PROTO_ATA) {
if (path->device->protocol == PROTO_ATA) {
if (ata_dma == 0 && (mode == 0 || mode > ATA_PIO_MAX))
mode = ATA_PIO_MAX;
} else {
@ -459,11 +460,13 @@ negotiate:
if (mode != wantmode)
goto negotiate;
/* Remember what transport thinks about DMA. */
oif = path->device->inq_flags;
if (mode < ATA_DMA)
path->device->inq_flags &= ~SID_DMA;
else
path->device->inq_flags |= SID_DMA;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (path->device->inq_flags != oif)
xpt_async(AC_GETDEV_CHANGED, path, NULL);
cam_fill_ataio(ataio,
1,
probedone,
@ -516,11 +519,13 @@ negotiate:
break;
case PROBE_SETAN:
/* Remember what transport thinks about AEN. */
oif = path->device->inq_flags;
if (softc->caps & CTS_SATA_CAPS_H_AN)
path->device->inq_flags |= SID_AEN;
else
path->device->inq_flags &= ~SID_AEN;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (path->device->inq_flags != oif)
xpt_async(AC_GETDEV_CHANGED, path, NULL);
cam_fill_ataio(ataio,
1,
probedone,
@ -639,7 +644,7 @@ negotiate:
{
u_int inquiry_len;
struct scsi_inquiry_data *inq_buf =
&periph->path->device->inq_data;
&path->device->inq_data;
if (softc->action == PROBE_INQUIRY)
inquiry_len = SHORT_INQUIRY_LENGTH;
@ -744,8 +749,8 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
struct cam_path *path;
cam_status status;
u_int32_t priority;
u_int caps;
int changed = 1, found = 1;
u_int caps, oif;
int changed, found = 1;
static const uint8_t fake_device_id_hdr[8] =
{0, SVPD_DEVICE_ID, 0, 12,
SVPD_ID_CODESET_BINARY, SVPD_ID_TYPE_NAA, 0, 8};
@ -922,23 +927,32 @@ noerror:
goto out;
}
ident_buf = &path->device->ident_data;
/* Check that it is the same device as we know. */
if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
/* Check that it is the same device. */
if (bcmp(softc->ident_data.model, ident_buf->model,
sizeof(ident_buf->model)) ||
bcmp(softc->ident_data.revision, ident_buf->revision,
sizeof(ident_buf->revision)) ||
bcmp(softc->ident_data.serial, ident_buf->serial,
sizeof(ident_buf->serial))) {
/* Device changed. */
/* The device was replaced. */
changed = 2;
xpt_async(AC_LOST_DEVICE, path, NULL);
} else if (bcmp(&softc->ident_data, ident_buf,
sizeof(*ident_buf))) {
/* The device is the same, but has changed. */
changed = 1;
} else {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
/* Nothing has changed. */
changed = 0;
}
} else {
/* This is a new device. */
changed = 2;
}
if (changed) {
if (changed != 0)
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
if (changed == 2) {
/* Clean up from previous instance of this device */
if (path->device->serial_num != NULL) {
free(path->device->serial_num, M_CAMXPT);
@ -975,10 +989,10 @@ noerror:
ata_bswap(path->device->device_id + 8, 8);
}
}
path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
}
if (changed == 1)
xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (ident_buf->satacapabilities & ATA_SUPPORT_NCQ) {
path->device->mintags = 2;
path->device->maxtags =
@ -1002,7 +1016,7 @@ noerror:
}
}
ata_device_transport(path);
if (changed)
if (changed == 2)
proberequestdefaultnegotiation(periph);
PROBE_SET_ACTION(softc, PROBE_SETMODE);
xpt_release_ccb(done_ccb);
@ -1058,6 +1072,7 @@ noerror:
* capability information is not provided or transport is
* SATA, we take support for granted.
*/
oif = path->device->inq_flags;
if (!(path->device->inq_flags & SID_DMA) ||
(path->device->transport == XPORT_ATA &&
(cts.xport_specific.ata.valid & CTS_ATA_VALID_CAPS) &&
@ -1065,6 +1080,8 @@ noerror:
path->device->inq_flags &= ~SID_DMA48;
else
path->device->inq_flags |= SID_DMA48;
if (path->device->inq_flags != oif)
xpt_async(AC_GETDEV_CHANGED, path, NULL);
/* Store result to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
@ -1235,6 +1252,7 @@ notsata:
else
caps = 0;
/* Remember what transport thinks about AEN. */
oif = path->device->inq_flags;
if ((caps & CTS_SATA_CAPS_H_AN) && path->device->protocol != PROTO_ATA)
path->device->inq_flags |= SID_AEN;
else
@ -1248,7 +1266,6 @@ notsata:
cts.xport_specific.sata.valid = CTS_SATA_VALID_CAPS;
xpt_action((union ccb *)&cts);
softc->caps = caps;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
@ -1256,6 +1273,8 @@ notsata:
xpt_action(done_ccb);
xpt_async(AC_FOUND_DEVICE, path, done_ccb);
} else {
if (path->device->inq_flags != oif)
xpt_async(AC_GETDEV_CHANGED, path, NULL);
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_SCSI_AEN, path, done_ccb);
@ -1268,12 +1287,14 @@ notsata:
/* Check that it is the same device. */
if (bcmp(&softc->ident_data, ident_buf, 53)) {
/* Device changed. */
changed = 2;
xpt_async(AC_LOST_DEVICE, path, NULL);
} else {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
changed = 0;
}
}
} else
changed = 2;
if (changed) {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
/* Clean up from previous instance of this device */