diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-07-05 08:18:21 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2012-08-17 14:10:36 -0400 |
commit | 1b26d29ccd592ea585c7cc291384184c5568da92 (patch) | |
tree | 561574fb2d7d1901134044f5f4d4340ade3ce07e | |
parent | 6ca8e79466d34874c188906e775c8f1f8c89b67a (diff) |
[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>
-rw-r--r-- | drivers/ata/libata-scsi.c | 194 |
1 files changed, 188 insertions, 6 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 67d23bd87950..e3bda074fa12 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -2243,7 +2243,7 @@ static void modecpy(u8 *dest, const u8 *src, int n, bool changeable) | |||
2243 | static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) | 2243 | static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) |
2244 | { | 2244 | { |
2245 | modecpy(buf, def_cache_mpage, sizeof(def_cache_mpage), changeable); | 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 | buf[2] |= (1 << 2); /* write cache enable */ | 2247 | buf[2] |= (1 << 2); /* write cache enable */ |
2248 | if (!changeable && !ata_id_rahead_enabled(id)) | 2248 | if (!changeable && !ata_id_rahead_enabled(id)) |
2249 | buf[12] |= (1 << 5); /* disable read ahead */ | 2249 | buf[12] |= (1 << 5); /* disable read ahead */ |
@@ -3107,6 +3107,188 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) | |||
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 | * ata_get_xlat_func - check if SCSI to ATA translation is possible | 3292 | * ata_get_xlat_func - check if SCSI to ATA translation is possible |
3111 | * @dev: ATA device | 3293 | * @dev: ATA device |
3112 | * @cmd: SCSI command opcode to consider | 3294 | * @cmd: SCSI command opcode to consider |
@@ -3146,6 +3328,11 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) | |||
3146 | case ATA_16: | 3328 | case ATA_16: |
3147 | return ata_scsi_pass_thru; | 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 | case START_STOP: | 3336 | case START_STOP: |
3150 | return ata_scsi_start_stop_xlat; | 3337 | return ata_scsi_start_stop_xlat; |
3151 | } | 3338 | } |
@@ -3338,11 +3525,6 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) | |||
3338 | ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); | 3525 | ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); |
3339 | break; | 3526 | break; |
3340 | 3527 | ||
3341 | case MODE_SELECT: /* unconditionally return */ | ||
3342 | case MODE_SELECT_10: /* bad-field-in-cdb */ | ||
3343 | ata_scsi_invalid_field(cmd); | ||
3344 | break; | ||
3345 | |||
3346 | case READ_CAPACITY: | 3528 | case READ_CAPACITY: |
3347 | ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); | 3529 | ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); |
3348 | break; | 3530 | break; |