diff --git a/sbin/camcontrol/fwdownload.c b/sbin/camcontrol/fwdownload.c --- a/sbin/camcontrol/fwdownload.c +++ b/sbin/camcontrol/fwdownload.c @@ -57,12 +57,15 @@ #include #include +#include #include #include #include #include +#include #include +#include #include #include @@ -759,6 +762,67 @@ return (retval); } +/* + * After the firmware is downloaded, we know the sense data has changed (or is + * likely to change since it contains the firmware version). Rescan the target + * with a flag to tell the kernel it's OK. This allows us to continnue using the + * old periph/disk in the kernel, which is less disruptive. We rescan the target + * because multilun devices usually update all the luns after the first firmware + * download. + */ +static int +fw_rescan_lun(struct cam_device *dev, bool printerrors) +{ + union ccb ccb; + int fd; + target_id_t target; + uint32_t bus; + + /* Can only send XPT_SCAN_TGT via /dev/xpt, not pass device in *dev */ + if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) { + warnx("error opening transport layer device %s\n", + XPT_DEVICE); + warn("%s", XPT_DEVICE); + return (1); + } + + /* Fill in the bus and target IDs as they don't seem to be in *dev */ + bzero(&ccb, sizeof(union ccb)); + ccb.ccb_h.func_code = XPT_GDEVLIST; + strlcpy(ccb.cgdl.periph_name, dev->device_name, sizeof(ccb.cgdl.periph_name)); + ccb.cgdl.unit_number = dev->dev_unit_num; + if (cam_send_ccb(dev, &ccb) < 0) { + warn("send_ccb GDEVLIST failed\n"); + close(fd); + return (1); + } + bus = ccb.ccb_h.path_id; + target = ccb.ccb_h.target_id; + + /* Rescan the target */ + bzero(&ccb, sizeof(union ccb)); + ccb.ccb_h.func_code = XPT_SCAN_TGT; + ccb.ccb_h.path_id = bus; + ccb.ccb_h.target_id = target; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + ccb.crcn.flags = CAM_EXPECT_INQ_CHANGE; + ccb.ccb_h.pinfo.priority = 5; /* run this at a low priority */ + + if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { + warn("CAMIOCOMMAND XPT_SCAN_TGT ioctl failed"); + close(fd); + return (1); + } + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warn("Can't send rescan lun"); + if (printerrors) + cam_error_print(dev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, + stderr); + return (1); + } + return (0); +} + /* * Download firmware stored in buf to cam_dev. If simulation mode * is enabled, only show what packet sizes would be sent to the @@ -912,6 +976,9 @@ if (quiet == 0) progress_complete(&progress, size - img_size); cam_freeccb(ccb); + if (retval == 0 && !sim_mode) { + fw_rescan_lun(cam_dev, printerrors); + } return (retval); }