Add timeout logic to sdhci, separate from the timeouts done by the hardware.

If the hardware is not in a good state (like maybe clocks aren't running
because of a configuration glitch) its timeout clock may also not work
correctly, and the next command sent will hang that thread forever.  The
thread in question is usually the one and only thread (at init time) or
a bio queue worker thread whose lockup will eventually lead to the whole
system locking up when it runs out of buffers.

No sd card command should take longer than 250ms.  This new code establishes
a 1-second timeout to allow plenty of safety margin over that.
This commit is contained in:
Ian Lepore 2014-02-15 20:45:53 +00:00
parent c4ec9d1887
commit e64f01a94a
2 changed files with 36 additions and 14 deletions

View File

@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/callout.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@ -609,6 +610,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
callout_init(&slot->card_callout, 1);
callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0);
return (0);
}
@ -623,6 +625,7 @@ sdhci_cleanup_slot(struct sdhci_slot *slot)
{
device_t d;
callout_drain(&slot->timeout_callout);
callout_drain(&slot->card_callout);
taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
@ -702,6 +705,32 @@ sdhci_generic_update_ios(device_t brdev, device_t reqdev)
return (0);
}
static void
sdhci_req_done(struct sdhci_slot *slot)
{
struct mmc_request *req;
if (slot->req != NULL && slot->curcmd != NULL) {
callout_stop(&slot->timeout_callout);
req = slot->req;
slot->req = NULL;
slot->curcmd = NULL;
req->done(req);
}
}
static void
sdhci_timeout(void *arg)
{
struct sdhci_slot *slot = arg;
if (slot->curcmd != NULL) {
sdhci_reset(slot, SDHCI_RESET_ALL);
slot->curcmd->error = MMC_ERR_TIMEOUT;
sdhci_req_done(slot);
}
}
static void
sdhci_set_transfer_mode(struct sdhci_slot *slot,
struct mmc_data *data)
@ -727,7 +756,6 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot,
static void
sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
{
struct mmc_request *req = slot->req;
int flags, timeout;
uint32_t mask, state;
@ -740,9 +768,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
slot_printf(slot, "Unsupported response type!\n");
cmd->error = MMC_ERR_FAILED;
slot->req = NULL;
slot->curcmd = NULL;
req->done(req);
sdhci_req_done(slot);
return;
}
@ -754,9 +780,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
slot->power == 0 ||
slot->clock == 0) {
cmd->error = MMC_ERR_FAILED;
slot->req = NULL;
slot->curcmd = NULL;
req->done(req);
sdhci_req_done(slot);
return;
}
/* Always wait for free CMD bus. */
@ -784,9 +808,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
"inhibit bit(s).\n");
sdhci_dumpregs(slot);
cmd->error = MMC_ERR_FAILED;
slot->req = NULL;
slot->curcmd = NULL;
req->done(req);
sdhci_req_done(slot);
return;
}
timeout--;
@ -828,6 +850,8 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
sdhci_set_transfer_mode(slot, cmd->data);
/* Start command. */
WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
/* Start timeout callout; no command should take more than a second. */
callout_reset(&slot->timeout_callout, hz, sdhci_timeout, slot);
}
static void
@ -1013,10 +1037,7 @@ sdhci_start(struct sdhci_slot *slot)
sdhci_reset(slot, SDHCI_RESET_DATA);
}
/* We must be done -- bad idea to do this while locked? */
slot->req = NULL;
slot->curcmd = NULL;
req->done(req);
sdhci_req_done(slot);
}
int

View File

@ -241,6 +241,7 @@ struct sdhci_slot {
bus_addr_t paddr; /* DMA buffer address */
struct task card_task; /* Card presence check task */
struct callout card_callout; /* Card insert delay callout */
struct callout timeout_callout;/* Card command/data response timeout */
struct mmc_host host; /* Host parameters */
struct mmc_request *req; /* Current request */
struct mmc_command *curcmd; /* Current command of current request */