Commit 1b26d29ccd592ea585c7cc291384184c5568da92
Committed by
Jeff Garzik
1 parent
6ca8e79466
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
[libata] scsi: implement MODE SELECT command
The cache_type file in sysfs lets users configure the disk cache in write-through or write-back modes. However, ata disks do not support writing to the file because they do not implement the MODE SELECT command. This patch adds a translation from MODE SELECT (for the caching page only) to the ATA SET FEATURES command. The set of changeable parameters answered by MODE SENSE is also adjusted accordingly. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Showing 1 changed file with 188 additions and 6 deletions Side-by-side Diff
drivers/ata/libata-scsi.c
... | ... | @@ -2243,7 +2243,7 @@ |
2243 | 2243 | static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) |
2244 | 2244 | { |
2245 | 2245 | modecpy(buf, def_cache_mpage, sizeof(def_cache_mpage), changeable); |
2246 | - if (!changeable && ata_id_wcache_enabled(id)) | |
2246 | + if (changeable || ata_id_wcache_enabled(id)) | |
2247 | 2247 | buf[2] |= (1 << 2); /* write cache enable */ |
2248 | 2248 | if (!changeable && !ata_id_rahead_enabled(id)) |
2249 | 2249 | buf[12] |= (1 << 5); /* disable read ahead */ |
... | ... | @@ -3107,6 +3107,188 @@ |
3107 | 3107 | } |
3108 | 3108 | |
3109 | 3109 | /** |
3110 | + * ata_mselect_caching - Simulate MODE SELECT for caching info page | |
3111 | + * @qc: Storage for translated ATA taskfile | |
3112 | + * @buf: input buffer | |
3113 | + * @len: number of valid bytes in the input buffer | |
3114 | + * | |
3115 | + * Prepare a taskfile to modify caching information for the device. | |
3116 | + * | |
3117 | + * LOCKING: | |
3118 | + * None. | |
3119 | + */ | |
3120 | +static int ata_mselect_caching(struct ata_queued_cmd *qc, | |
3121 | + const u8 *buf, int len) | |
3122 | +{ | |
3123 | + struct ata_taskfile *tf = &qc->tf; | |
3124 | + struct ata_device *dev = qc->dev; | |
3125 | + char mpage[CACHE_MPAGE_LEN]; | |
3126 | + u8 wce; | |
3127 | + | |
3128 | + /* | |
3129 | + * The first two bytes of def_cache_mpage are a header, so offsets | |
3130 | + * in mpage are off by 2 compared to buf. Same for len. | |
3131 | + */ | |
3132 | + | |
3133 | + if (len != CACHE_MPAGE_LEN - 2) | |
3134 | + return -EINVAL; | |
3135 | + | |
3136 | + wce = buf[0] & (1 << 2); | |
3137 | + | |
3138 | + /* | |
3139 | + * Check that read-only bits are not modified. | |
3140 | + */ | |
3141 | + ata_msense_caching(dev->id, mpage, false); | |
3142 | + mpage[2] &= ~(1 << 2); | |
3143 | + mpage[2] |= wce; | |
3144 | + if (memcmp(mpage + 2, buf, CACHE_MPAGE_LEN - 2) != 0) | |
3145 | + return -EINVAL; | |
3146 | + | |
3147 | + tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; | |
3148 | + tf->protocol = ATA_PROT_NODATA; | |
3149 | + tf->nsect = 0; | |
3150 | + tf->command = ATA_CMD_SET_FEATURES; | |
3151 | + tf->feature = wce ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF; | |
3152 | + return 0; | |
3153 | +} | |
3154 | + | |
3155 | +/** | |
3156 | + * ata_scsiop_mode_select - Simulate MODE SELECT 6, 10 commands | |
3157 | + * @qc: Storage for translated ATA taskfile | |
3158 | + * | |
3159 | + * Converts a MODE SELECT command to an ATA SET FEATURES taskfile. | |
3160 | + * Assume this is invoked for direct access devices (e.g. disks) only. | |
3161 | + * There should be no block descriptor for other device types. | |
3162 | + * | |
3163 | + * LOCKING: | |
3164 | + * spin_lock_irqsave(host lock) | |
3165 | + */ | |
3166 | +static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) | |
3167 | +{ | |
3168 | + struct scsi_cmnd *scmd = qc->scsicmd; | |
3169 | + const u8 *cdb = scmd->cmnd; | |
3170 | + const u8 *p; | |
3171 | + u8 pg, spg; | |
3172 | + unsigned six_byte, pg_len, hdr_len, bd_len; | |
3173 | + int len; | |
3174 | + | |
3175 | + VPRINTK("ENTER\n"); | |
3176 | + | |
3177 | + six_byte = (cdb[0] == MODE_SELECT); | |
3178 | + if (six_byte) { | |
3179 | + if (scmd->cmd_len < 5) | |
3180 | + goto invalid_fld; | |
3181 | + | |
3182 | + len = cdb[4]; | |
3183 | + hdr_len = 4; | |
3184 | + } else { | |
3185 | + if (scmd->cmd_len < 9) | |
3186 | + goto invalid_fld; | |
3187 | + | |
3188 | + len = (cdb[7] << 8) + cdb[8]; | |
3189 | + hdr_len = 8; | |
3190 | + } | |
3191 | + | |
3192 | + /* We only support PF=1, SP=0. */ | |
3193 | + if ((cdb[1] & 0x11) != 0x10) | |
3194 | + goto invalid_fld; | |
3195 | + | |
3196 | + /* Test early for possible overrun. */ | |
3197 | + if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len) | |
3198 | + goto invalid_param_len; | |
3199 | + | |
3200 | + p = page_address(sg_page(scsi_sglist(scmd))); | |
3201 | + | |
3202 | + /* Move past header and block descriptors. */ | |
3203 | + if (len < hdr_len) | |
3204 | + goto invalid_param_len; | |
3205 | + | |
3206 | + if (six_byte) | |
3207 | + bd_len = p[3]; | |
3208 | + else | |
3209 | + bd_len = (p[6] << 8) + p[7]; | |
3210 | + | |
3211 | + len -= hdr_len; | |
3212 | + p += hdr_len; | |
3213 | + if (len < bd_len) | |
3214 | + goto invalid_param_len; | |
3215 | + if (bd_len != 0 && bd_len != 8) | |
3216 | + goto invalid_param; | |
3217 | + | |
3218 | + len -= bd_len; | |
3219 | + p += bd_len; | |
3220 | + if (len == 0) | |
3221 | + goto skip; | |
3222 | + | |
3223 | + /* Parse both possible formats for the mode page headers. */ | |
3224 | + pg = p[0] & 0x3f; | |
3225 | + if (p[0] & 0x40) { | |
3226 | + if (len < 4) | |
3227 | + goto invalid_param_len; | |
3228 | + | |
3229 | + spg = p[1]; | |
3230 | + pg_len = (p[2] << 8) | p[3]; | |
3231 | + p += 4; | |
3232 | + len -= 4; | |
3233 | + } else { | |
3234 | + if (len < 2) | |
3235 | + goto invalid_param_len; | |
3236 | + | |
3237 | + spg = 0; | |
3238 | + pg_len = p[1]; | |
3239 | + p += 2; | |
3240 | + len -= 2; | |
3241 | + } | |
3242 | + | |
3243 | + /* | |
3244 | + * No mode subpages supported (yet) but asking for _all_ | |
3245 | + * subpages may be valid | |
3246 | + */ | |
3247 | + if (spg && (spg != ALL_SUB_MPAGES)) | |
3248 | + goto invalid_param; | |
3249 | + if (pg_len > len) | |
3250 | + goto invalid_param_len; | |
3251 | + | |
3252 | + switch (pg) { | |
3253 | + case CACHE_MPAGE: | |
3254 | + if (ata_mselect_caching(qc, p, pg_len) < 0) | |
3255 | + goto invalid_param; | |
3256 | + break; | |
3257 | + | |
3258 | + default: /* invalid page code */ | |
3259 | + goto invalid_param; | |
3260 | + } | |
3261 | + | |
3262 | + /* | |
3263 | + * Only one page has changeable data, so we only support setting one | |
3264 | + * page at a time. | |
3265 | + */ | |
3266 | + if (len > pg_len) | |
3267 | + goto invalid_param; | |
3268 | + | |
3269 | + return 0; | |
3270 | + | |
3271 | + invalid_fld: | |
3272 | + /* "Invalid field in CDB" */ | |
3273 | + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); | |
3274 | + return 1; | |
3275 | + | |
3276 | + invalid_param: | |
3277 | + /* "Invalid field in parameter list" */ | |
3278 | + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0); | |
3279 | + return 1; | |
3280 | + | |
3281 | + invalid_param_len: | |
3282 | + /* "Parameter list length error" */ | |
3283 | + ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x1a, 0x0); | |
3284 | + return 1; | |
3285 | + | |
3286 | + skip: | |
3287 | + scmd->result = SAM_STAT_GOOD; | |
3288 | + return 1; | |
3289 | +} | |
3290 | + | |
3291 | +/** | |
3110 | 3292 | * ata_get_xlat_func - check if SCSI to ATA translation is possible |
3111 | 3293 | * @dev: ATA device |
3112 | 3294 | * @cmd: SCSI command opcode to consider |
... | ... | @@ -3146,6 +3328,11 @@ |
3146 | 3328 | case ATA_16: |
3147 | 3329 | return ata_scsi_pass_thru; |
3148 | 3330 | |
3331 | + case MODE_SELECT: | |
3332 | + case MODE_SELECT_10: | |
3333 | + return ata_scsi_mode_select_xlat; | |
3334 | + break; | |
3335 | + | |
3149 | 3336 | case START_STOP: |
3150 | 3337 | return ata_scsi_start_stop_xlat; |
3151 | 3338 | } |
... | ... | @@ -3336,11 +3523,6 @@ |
3336 | 3523 | case MODE_SENSE: |
3337 | 3524 | case MODE_SENSE_10: |
3338 | 3525 | ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); |
3339 | - break; | |
3340 | - | |
3341 | - case MODE_SELECT: /* unconditionally return */ | |
3342 | - case MODE_SELECT_10: /* bad-field-in-cdb */ | |
3343 | - ata_scsi_invalid_field(cmd); | |
3344 | 3526 | break; |
3345 | 3527 | |
3346 | 3528 | case READ_CAPACITY: |