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 | |
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')
-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, |