diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 79 |
1 files changed, 61 insertions, 18 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 1c69c14be0cd..953773cb26d9 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c | |||
@@ -2022,16 +2022,26 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) | |||
2022 | 2022 | ||
2023 | int dbd; | 2023 | int dbd; |
2024 | int modepage; | 2024 | int modepage; |
2025 | int first_len; | ||
2025 | struct scsi_mode_data data; | 2026 | struct scsi_mode_data data; |
2026 | struct scsi_sense_hdr sshdr; | 2027 | struct scsi_sense_hdr sshdr; |
2027 | int old_wce = sdkp->WCE; | 2028 | int old_wce = sdkp->WCE; |
2028 | int old_rcd = sdkp->RCD; | 2029 | int old_rcd = sdkp->RCD; |
2029 | int old_dpofua = sdkp->DPOFUA; | 2030 | int old_dpofua = sdkp->DPOFUA; |
2030 | 2031 | ||
2031 | if (sdp->skip_ms_page_8) | 2032 | first_len = 4; |
2032 | goto defaults; | 2033 | if (sdp->skip_ms_page_8) { |
2033 | 2034 | if (sdp->type == TYPE_RBC) | |
2034 | if (sdp->type == TYPE_RBC) { | 2035 | goto defaults; |
2036 | else { | ||
2037 | if (sdp->skip_ms_page_3f) | ||
2038 | goto defaults; | ||
2039 | modepage = 0x3F; | ||
2040 | if (sdp->use_192_bytes_for_3f) | ||
2041 | first_len = 192; | ||
2042 | dbd = 0; | ||
2043 | } | ||
2044 | } else if (sdp->type == TYPE_RBC) { | ||
2035 | modepage = 6; | 2045 | modepage = 6; |
2036 | dbd = 8; | 2046 | dbd = 8; |
2037 | } else { | 2047 | } else { |
@@ -2040,13 +2050,15 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) | |||
2040 | } | 2050 | } |
2041 | 2051 | ||
2042 | /* cautiously ask */ | 2052 | /* cautiously ask */ |
2043 | res = sd_do_mode_sense(sdp, dbd, modepage, buffer, 4, &data, &sshdr); | 2053 | res = sd_do_mode_sense(sdp, dbd, modepage, buffer, first_len, |
2054 | &data, &sshdr); | ||
2044 | 2055 | ||
2045 | if (!scsi_status_is_good(res)) | 2056 | if (!scsi_status_is_good(res)) |
2046 | goto bad_sense; | 2057 | goto bad_sense; |
2047 | 2058 | ||
2048 | if (!data.header_length) { | 2059 | if (!data.header_length) { |
2049 | modepage = 6; | 2060 | modepage = 6; |
2061 | first_len = 0; | ||
2050 | sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response\n"); | 2062 | sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response\n"); |
2051 | } | 2063 | } |
2052 | 2064 | ||
@@ -2059,30 +2071,61 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) | |||
2059 | */ | 2071 | */ |
2060 | if (len < 3) | 2072 | if (len < 3) |
2061 | goto bad_sense; | 2073 | goto bad_sense; |
2062 | if (len > 20) | 2074 | else if (len > SD_BUF_SIZE) { |
2063 | len = 20; | 2075 | sd_printk(KERN_NOTICE, sdkp, "Truncating mode parameter " |
2064 | 2076 | "data from %d to %d bytes\n", len, SD_BUF_SIZE); | |
2065 | /* Take headers and block descriptors into account */ | 2077 | len = SD_BUF_SIZE; |
2066 | len += data.header_length + data.block_descriptor_length; | 2078 | } |
2067 | if (len > SD_BUF_SIZE) | 2079 | if (modepage == 0x3F && sdp->use_192_bytes_for_3f) |
2068 | goto bad_sense; | 2080 | len = 192; |
2069 | 2081 | ||
2070 | /* Get the data */ | 2082 | /* Get the data */ |
2071 | res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); | 2083 | if (len > first_len) |
2084 | res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, | ||
2085 | &data, &sshdr); | ||
2072 | 2086 | ||
2073 | if (scsi_status_is_good(res)) { | 2087 | if (scsi_status_is_good(res)) { |
2074 | int offset = data.header_length + data.block_descriptor_length; | 2088 | int offset = data.header_length + data.block_descriptor_length; |
2075 | 2089 | ||
2076 | if (offset >= SD_BUF_SIZE - 2) { | 2090 | while (offset < len) { |
2077 | sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response\n"); | 2091 | u8 page_code = buffer[offset] & 0x3F; |
2078 | goto defaults; | 2092 | u8 spf = buffer[offset] & 0x40; |
2093 | |||
2094 | if (page_code == 8 || page_code == 6) { | ||
2095 | /* We're interested only in the first 3 bytes. | ||
2096 | */ | ||
2097 | if (len - offset <= 2) { | ||
2098 | sd_printk(KERN_ERR, sdkp, "Incomplete " | ||
2099 | "mode parameter data\n"); | ||
2100 | goto defaults; | ||
2101 | } else { | ||
2102 | modepage = page_code; | ||
2103 | goto Page_found; | ||
2104 | } | ||
2105 | } else { | ||
2106 | /* Go to the next page */ | ||
2107 | if (spf && len - offset > 3) | ||
2108 | offset += 4 + (buffer[offset+2] << 8) + | ||
2109 | buffer[offset+3]; | ||
2110 | else if (!spf && len - offset > 1) | ||
2111 | offset += 2 + buffer[offset+1]; | ||
2112 | else { | ||
2113 | sd_printk(KERN_ERR, sdkp, "Incomplete " | ||
2114 | "mode parameter data\n"); | ||
2115 | goto defaults; | ||
2116 | } | ||
2117 | } | ||
2079 | } | 2118 | } |
2080 | 2119 | ||
2081 | if ((buffer[offset] & 0x3f) != modepage) { | 2120 | if (modepage == 0x3F) { |
2121 | sd_printk(KERN_ERR, sdkp, "No Caching mode page " | ||
2122 | "present\n"); | ||
2123 | goto defaults; | ||
2124 | } else if ((buffer[offset] & 0x3f) != modepage) { | ||
2082 | sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); | 2125 | sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); |
2083 | goto defaults; | 2126 | goto defaults; |
2084 | } | 2127 | } |
2085 | 2128 | Page_found: | |
2086 | if (modepage == 8) { | 2129 | if (modepage == 8) { |
2087 | sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); | 2130 | sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); |
2088 | sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); | 2131 | sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); |