Index: head/sys/cam/scsi/scsi_da.c =================================================================== --- head/sys/cam/scsi/scsi_da.c +++ head/sys/cam/scsi/scsi_da.c @@ -81,6 +81,7 @@ * ATA -> LOGDIR -> IDDIR -> SUP -> ATA_ZONE */ typedef enum { + DA_STATE_PROBE_WP, DA_STATE_PROBE_RC, DA_STATE_PROBE_RC16, DA_STATE_PROBE_LBP, @@ -157,6 +158,7 @@ DA_CCB_PROBE_ATA_IDDIR = 0x0F, DA_CCB_PROBE_ATA_SUP = 0x10, DA_CCB_PROBE_ATA_ZONE = 0x11, + DA_CCB_PROBE_WP = 0x12, DA_CCB_TYPE_MASK = 0x1F, DA_CCB_RETRY_UA = 0x20 } da_ccb_state; @@ -2427,7 +2429,7 @@ } LIST_INIT(&softc->pending_ccbs); - softc->state = DA_STATE_PROBE_RC; + softc->state = DA_STATE_PROBE_WP; bioq_init(&softc->delete_run_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= DA_FLAG_PACK_REMOVABLE; @@ -2526,7 +2528,6 @@ if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 && (softc->quirks & DA_Q_NO_RC16) == 0) { softc->flags |= DA_FLAG_CAN_RC16; - softc->state = DA_STATE_PROBE_RC16; } /* @@ -3095,6 +3096,36 @@ daschedule(periph); break; } + case DA_STATE_PROBE_WP: + { + void *mode_buf; + int mode_buf_len; + + mode_buf_len = 192; + mode_buf = malloc(mode_buf_len, M_SCSIDA, M_NOWAIT); + if (mode_buf == NULL) { + xpt_print(periph->path, "Unable to send mode sense - " + "malloc failure\n"); + softc->state = DA_STATE_PROBE_RC; + goto skipstate; + } + scsi_mode_sense_len(&start_ccb->csio, + /*retries*/ da_retry_count, + /*cbfcnp*/ dadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*dbd*/ FALSE, + /*pc*/ SMS_PAGE_CTRL_CURRENT, + /*page*/ SMS_ALL_PAGES_PAGE, + /*param_buf*/ mode_buf, + /*param_len*/ mode_buf_len, + /*minimum_cmd_size*/ softc->minimum_cmd_size, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ da_default_timeout * 1000); + start_ccb->ccb_h.ccb_bp = NULL; + start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_WP; + xpt_action(start_ccb); + break; + } case DA_STATE_PROBE_RC: { struct scsi_read_capacity_data *rcap; @@ -4255,6 +4286,52 @@ biodone(bp); return; } + case DA_CCB_PROBE_WP: + { + struct scsi_mode_header_6 *mode_hdr6; + struct scsi_mode_header_10 *mode_hdr10; + uint8_t dev_spec; + + if (softc->minimum_cmd_size > 6) { + mode_hdr10 = (struct scsi_mode_header_10 *)csio->data_ptr; + dev_spec = mode_hdr10->dev_spec; + } else { + mode_hdr6 = (struct scsi_mode_header_6 *)csio->data_ptr; + dev_spec = mode_hdr6->dev_spec; + } + if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) { + if ((dev_spec & 0x80) != 0) + softc->disk->d_flags |= DISKFLAG_WRITE_PROTECT; + else + softc->disk->d_flags &= ~DISKFLAG_WRITE_PROTECT; + } else { + int error; + + error = daerror(done_ccb, CAM_RETRY_SELTO, + SF_RETRY_UA|SF_NO_PRINT); + if (error == ERESTART) + return; + else if (error != 0) { + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge this device's queue */ + cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/0, + /*reduction*/0, + /*timeout*/0, + /*getcount_only*/0); + } + } + } + + free(csio->data_ptr, M_SCSIDA); + xpt_release_ccb(done_ccb); + if ((softc->flags & DA_FLAG_CAN_RC16) != 0) + softc->state = DA_STATE_PROBE_RC16; + else + softc->state = DA_STATE_PROBE_RC; + xpt_schedule(periph, priority); + return; + } case DA_CCB_PROBE_RC: case DA_CCB_PROBE_RC16: { @@ -5340,11 +5417,7 @@ KASSERT(status == CAM_REQ_CMP, ("dareprobe: cam_periph_acquire failed")); - if (softc->flags & DA_FLAG_CAN_RC16) - softc->state = DA_STATE_PROBE_RC16; - else - softc->state = DA_STATE_PROBE_RC; - + softc->state = DA_STATE_PROBE_WP; xpt_schedule(periph, CAM_PRIORITY_DEV); } Index: head/sys/geom/geom_disk.h =================================================================== --- head/sys/geom/geom_disk.h +++ head/sys/geom/geom_disk.h @@ -126,13 +126,14 @@ LIST_HEAD(,disk_alias) d_aliases; }; -#define DISKFLAG_RESERVED 0x1 /* Was NEEDSGIANT */ -#define DISKFLAG_OPEN 0x2 -#define DISKFLAG_CANDELETE 0x4 -#define DISKFLAG_CANFLUSHCACHE 0x8 -#define DISKFLAG_UNMAPPED_BIO 0x10 -#define DISKFLAG_DIRECT_COMPLETION 0x20 -#define DISKFLAG_CANZONE 0x80 +#define DISKFLAG_RESERVED 0x0001 /* Was NEEDSGIANT */ +#define DISKFLAG_OPEN 0x0002 +#define DISKFLAG_CANDELETE 0x0004 +#define DISKFLAG_CANFLUSHCACHE 0x0008 +#define DISKFLAG_UNMAPPED_BIO 0x0010 +#define DISKFLAG_DIRECT_COMPLETION 0x0020 +#define DISKFLAG_CANZONE 0x0080 +#define DISKFLAG_WRITE_PROTECT 0x0100 struct disk *disk_alloc(void); void disk_create(struct disk *disk, int version); Index: head/sys/geom/geom_disk.c =================================================================== --- head/sys/geom/geom_disk.c +++ head/sys/geom/geom_disk.c @@ -122,14 +122,18 @@ e += pp->ace; error = 0; if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { - if (dp->d_open != NULL) { + /* + * It would be better to defer this decision to d_open if + * it was able to take flags. + */ + if (w > 0 && (dp->d_flags & DISKFLAG_WRITE_PROTECT) != 0) + error = EROFS; + if (error == 0 && dp->d_open != NULL) error = dp->d_open(dp); - if (bootverbose && error != 0) - printf("Opened disk %s -> %d\n", - pp->name, error); - if (error != 0) - return (error); - } + if (bootverbose && error != 0) + printf("Opened disk %s -> %d\n", pp->name, error); + if (error != 0) + return (error); pp->sectorsize = dp->d_sectorsize; if (dp->d_maxsize == 0) { printf("WARNING: Disk drive %s%d has no d_maxsize\n", @@ -1043,7 +1047,8 @@ "\4CANFLUSHCACHE" "\5UNMAPPEDBIO" "\6DIRECTCOMPLETION" - "\10CANZONE"); + "\10CANZONE" + "\11WRITEPROTECT"); sbuf_finish(sb); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);