diff options
| author | KOBAYASHI Yoshitake <yoshitake.kobayashi@toshiba.co.jp> | 2013-07-06 18:35:45 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2013-08-24 22:13:22 -0400 |
| commit | c8760069627ad3b0dbbea170f0c4c58b16e18d3d (patch) | |
| tree | ab3a9514d1ce33fd66bef9dad85b6a82e61217f9 /drivers/mmc/card | |
| parent | 2af502ca640969e335c02be3834b86c5b58be591 (diff) | |
mmc: block: fix a bug of error handling in MMC driver
Current MMC driver doesn't handle generic error (bit19 of device
status) in write sequence. As a result, write data gets lost when
generic error occurs. For example, a generic error when updating a
filesystem management information causes a loss of write data and
corrupts the filesystem. In the worst case, the system will never
boot.
This patch includes the following functionality:
1. To enable error checking for the response of CMD12 and CMD13
in write command sequence
2. To retry write sequence when a generic error occurs
Messages are added for v2 to show what occurs.
Signed-off-by: KOBAYASHI Yoshitake <yoshitake.kobayashi@toshiba.co.jp>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/card')
| -rw-r--r-- | drivers/mmc/card/block.c | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index cd0b7f4a1ff2..bebcb8e11efb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c | |||
| @@ -812,7 +812,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, | |||
| 812 | * Otherwise we don't understand what happened, so abort. | 812 | * Otherwise we don't understand what happened, so abort. |
| 813 | */ | 813 | */ |
| 814 | static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, | 814 | static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, |
| 815 | struct mmc_blk_request *brq, int *ecc_err) | 815 | struct mmc_blk_request *brq, int *ecc_err, int *gen_err) |
| 816 | { | 816 | { |
| 817 | bool prev_cmd_status_valid = true; | 817 | bool prev_cmd_status_valid = true; |
| 818 | u32 status, stop_status = 0; | 818 | u32 status, stop_status = 0; |
| @@ -850,6 +850,16 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, | |||
| 850 | (brq->cmd.resp[0] & R1_CARD_ECC_FAILED)) | 850 | (brq->cmd.resp[0] & R1_CARD_ECC_FAILED)) |
| 851 | *ecc_err = 1; | 851 | *ecc_err = 1; |
| 852 | 852 | ||
| 853 | /* Flag General errors */ | ||
| 854 | if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) | ||
| 855 | if ((status & R1_ERROR) || | ||
| 856 | (brq->stop.resp[0] & R1_ERROR)) { | ||
| 857 | pr_err("%s: %s: general error sending stop or status command, stop cmd response %#x, card status %#x\n", | ||
| 858 | req->rq_disk->disk_name, __func__, | ||
| 859 | brq->stop.resp[0], status); | ||
| 860 | *gen_err = 1; | ||
| 861 | } | ||
| 862 | |||
| 853 | /* | 863 | /* |
| 854 | * Check the current card state. If it is in some data transfer | 864 | * Check the current card state. If it is in some data transfer |
| 855 | * mode, tell it to stop (and hopefully transition back to TRAN.) | 865 | * mode, tell it to stop (and hopefully transition back to TRAN.) |
| @@ -869,6 +879,13 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, | |||
| 869 | return ERR_ABORT; | 879 | return ERR_ABORT; |
| 870 | if (stop_status & R1_CARD_ECC_FAILED) | 880 | if (stop_status & R1_CARD_ECC_FAILED) |
| 871 | *ecc_err = 1; | 881 | *ecc_err = 1; |
| 882 | if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) | ||
| 883 | if (stop_status & R1_ERROR) { | ||
| 884 | pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n", | ||
| 885 | req->rq_disk->disk_name, __func__, | ||
| 886 | stop_status); | ||
| 887 | *gen_err = 1; | ||
| 888 | } | ||
| 872 | } | 889 | } |
| 873 | 890 | ||
| 874 | /* Check for set block count errors */ | 891 | /* Check for set block count errors */ |
| @@ -1097,7 +1114,7 @@ static int mmc_blk_err_check(struct mmc_card *card, | |||
| 1097 | mmc_active); | 1114 | mmc_active); |
| 1098 | struct mmc_blk_request *brq = &mq_mrq->brq; | 1115 | struct mmc_blk_request *brq = &mq_mrq->brq; |
| 1099 | struct request *req = mq_mrq->req; | 1116 | struct request *req = mq_mrq->req; |
| 1100 | int ecc_err = 0; | 1117 | int ecc_err = 0, gen_err = 0; |
| 1101 | 1118 | ||
| 1102 | /* | 1119 | /* |
| 1103 | * sbc.error indicates a problem with the set block count | 1120 | * sbc.error indicates a problem with the set block count |
| @@ -1111,7 +1128,7 @@ static int mmc_blk_err_check(struct mmc_card *card, | |||
| 1111 | */ | 1128 | */ |
| 1112 | if (brq->sbc.error || brq->cmd.error || brq->stop.error || | 1129 | if (brq->sbc.error || brq->cmd.error || brq->stop.error || |
| 1113 | brq->data.error) { | 1130 | brq->data.error) { |
| 1114 | switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err)) { | 1131 | switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err, &gen_err)) { |
| 1115 | case ERR_RETRY: | 1132 | case ERR_RETRY: |
| 1116 | return MMC_BLK_RETRY; | 1133 | return MMC_BLK_RETRY; |
| 1117 | case ERR_ABORT: | 1134 | case ERR_ABORT: |
| @@ -1143,6 +1160,14 @@ static int mmc_blk_err_check(struct mmc_card *card, | |||
| 1143 | u32 status; | 1160 | u32 status; |
| 1144 | unsigned long timeout; | 1161 | unsigned long timeout; |
| 1145 | 1162 | ||
| 1163 | /* Check stop command response */ | ||
| 1164 | if (brq->stop.resp[0] & R1_ERROR) { | ||
| 1165 | pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n", | ||
| 1166 | req->rq_disk->disk_name, __func__, | ||
| 1167 | brq->stop.resp[0]); | ||
| 1168 | gen_err = 1; | ||
| 1169 | } | ||
| 1170 | |||
| 1146 | timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS); | 1171 | timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS); |
| 1147 | do { | 1172 | do { |
| 1148 | int err = get_card_status(card, &status, 5); | 1173 | int err = get_card_status(card, &status, 5); |
| @@ -1152,6 +1177,13 @@ static int mmc_blk_err_check(struct mmc_card *card, | |||
| 1152 | return MMC_BLK_CMD_ERR; | 1177 | return MMC_BLK_CMD_ERR; |
| 1153 | } | 1178 | } |
| 1154 | 1179 | ||
| 1180 | if (status & R1_ERROR) { | ||
| 1181 | pr_err("%s: %s: general error sending status command, card status %#x\n", | ||
| 1182 | req->rq_disk->disk_name, __func__, | ||
| 1183 | status); | ||
| 1184 | gen_err = 1; | ||
| 1185 | } | ||
| 1186 | |||
| 1155 | /* Timeout if the device never becomes ready for data | 1187 | /* Timeout if the device never becomes ready for data |
| 1156 | * and never leaves the program state. | 1188 | * and never leaves the program state. |
| 1157 | */ | 1189 | */ |
| @@ -1171,6 +1203,13 @@ static int mmc_blk_err_check(struct mmc_card *card, | |||
| 1171 | (R1_CURRENT_STATE(status) == R1_STATE_PRG)); | 1203 | (R1_CURRENT_STATE(status) == R1_STATE_PRG)); |
| 1172 | } | 1204 | } |
| 1173 | 1205 | ||
| 1206 | /* if general error occurs, retry the write operation. */ | ||
| 1207 | if (gen_err) { | ||
| 1208 | pr_warn("%s: retrying write for general error\n", | ||
| 1209 | req->rq_disk->disk_name); | ||
| 1210 | return MMC_BLK_RETRY; | ||
| 1211 | } | ||
| 1212 | |||
| 1174 | if (brq->data.error) { | 1213 | if (brq->data.error) { |
| 1175 | pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n", | 1214 | pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n", |
| 1176 | req->rq_disk->disk_name, brq->data.error, | 1215 | req->rq_disk->disk_name, brq->data.error, |
