aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2012-07-05 08:18:21 -0400
committerJeff Garzik <jgarzik@redhat.com>2012-08-17 14:10:36 -0400
commit1b26d29ccd592ea585c7cc291384184c5568da92 (patch)
tree561574fb2d7d1901134044f5f4d4340ade3ce07e
parent6ca8e79466d34874c188906e775c8f1f8c89b67a (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.c194
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)
2243static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) 2243static 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 */
3120static 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 */
3166static 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;