mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
mmc: restrict posted write counts for SD cards in CQ mode
Command Queueing requires Write Cache and Power off Notification support from the card - but using the write cache forms a contract with the host whereby the card expects to be told about impending power-down. The implication is that (for performance) the card can do unsafe things with pending write data - including reordering what gets committed to nonvolatile storage at what time. Exposed SD slots and platforms powered by hotpluggable means (i.e. Raspberry Pis) can't guarantee that surprise removal won't happen. To limit the scope for cards to invent new ways to trash filesystems, limit pending writes to 1 (equivalent to the non-CQ behaviour). Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com> fixup: mmc: restrict posted write counts for SD cards in CQ mode Leaving card->max_posted_writes unintialised was a bad thing to do. Also, cqe_enable is 1 if hsq is enabled as hsq substitutes the cqhci implementation with its own. Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
This commit is contained in:
committed by
Dom Cobley
parent
0850ccbe07
commit
2690b5a2d7
@@ -1581,6 +1581,8 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req)
|
|||||||
|
|
||||||
spin_lock_irqsave(&mq->lock, flags);
|
spin_lock_irqsave(&mq->lock, flags);
|
||||||
|
|
||||||
|
if (req_op(req) == REQ_OP_WRITE)
|
||||||
|
mq->pending_writes--;
|
||||||
mq->in_flight[issue_type] -= 1;
|
mq->in_flight[issue_type] -= 1;
|
||||||
|
|
||||||
put_card = (mmc_tot_in_flight(mq) == 0);
|
put_card = (mmc_tot_in_flight(mq) == 0);
|
||||||
@@ -2101,6 +2103,8 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req)
|
|||||||
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
|
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
|
||||||
unsigned int nr_bytes = mqrq->brq.data.bytes_xfered;
|
unsigned int nr_bytes = mqrq->brq.data.bytes_xfered;
|
||||||
|
|
||||||
|
if (req_op(req) == REQ_OP_WRITE)
|
||||||
|
mq->pending_writes--;
|
||||||
if (nr_bytes) {
|
if (nr_bytes) {
|
||||||
if (blk_update_request(req, BLK_STS_OK, nr_bytes))
|
if (blk_update_request(req, BLK_STS_OK, nr_bytes))
|
||||||
blk_mq_requeue_request(req, true);
|
blk_mq_requeue_request(req, true);
|
||||||
@@ -2195,13 +2199,16 @@ static void mmc_blk_mq_poll_completion(struct mmc_queue *mq,
|
|||||||
mmc_blk_urgent_bkops(mq, mqrq);
|
mmc_blk_urgent_bkops(mq, mqrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_blk_mq_dec_in_flight(struct mmc_queue *mq, enum mmc_issue_type issue_type)
|
static void mmc_blk_mq_dec_in_flight(struct mmc_queue *mq, enum mmc_issue_type issue_type,
|
||||||
|
struct request *req)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool put_card;
|
bool put_card;
|
||||||
|
|
||||||
spin_lock_irqsave(&mq->lock, flags);
|
spin_lock_irqsave(&mq->lock, flags);
|
||||||
|
|
||||||
|
if (req_op(req) == REQ_OP_WRITE)
|
||||||
|
mq->pending_writes--;
|
||||||
mq->in_flight[issue_type] -= 1;
|
mq->in_flight[issue_type] -= 1;
|
||||||
|
|
||||||
put_card = (mmc_tot_in_flight(mq) == 0);
|
put_card = (mmc_tot_in_flight(mq) == 0);
|
||||||
@@ -2235,7 +2242,7 @@ static void mmc_blk_mq_post_req(struct mmc_queue *mq, struct request *req,
|
|||||||
blk_mq_complete_request(req);
|
blk_mq_complete_request(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_blk_mq_dec_in_flight(mq, issue_type);
|
mmc_blk_mq_dec_in_flight(mq, issue_type, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mmc_blk_mq_recovery(struct mmc_queue *mq)
|
void mmc_blk_mq_recovery(struct mmc_queue *mq)
|
||||||
|
|||||||
@@ -1668,6 +1668,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
card->ocr = ocr;
|
card->ocr = ocr;
|
||||||
card->type = MMC_TYPE_MMC;
|
card->type = MMC_TYPE_MMC;
|
||||||
card->rca = 1;
|
card->rca = 1;
|
||||||
|
card->max_posted_writes = 1;
|
||||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1933,6 +1934,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
pr_info("%s: Host Software Queue enabled\n",
|
pr_info("%s: Host Software Queue enabled\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
}
|
}
|
||||||
|
card->max_posted_writes = card->ext_csd.cmdq_depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,11 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||||||
spin_unlock_irq(&mq->lock);
|
spin_unlock_irq(&mq->lock);
|
||||||
return BLK_STS_RESOURCE;
|
return BLK_STS_RESOURCE;
|
||||||
}
|
}
|
||||||
|
if (!host->hsq_enabled && host->cqe_enabled && req_op(req) == REQ_OP_WRITE &&
|
||||||
|
mq->pending_writes >= card->max_posted_writes) {
|
||||||
|
spin_unlock_irq(&mq->lock);
|
||||||
|
return BLK_STS_RESOURCE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
@@ -282,6 +287,8 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||||||
/* Parallel dispatch of requests is not supported at the moment */
|
/* Parallel dispatch of requests is not supported at the moment */
|
||||||
mq->busy = true;
|
mq->busy = true;
|
||||||
|
|
||||||
|
if (req_op(req) == REQ_OP_WRITE)
|
||||||
|
mq->pending_writes++;
|
||||||
mq->in_flight[issue_type] += 1;
|
mq->in_flight[issue_type] += 1;
|
||||||
get_card = (mmc_tot_in_flight(mq) == 1);
|
get_card = (mmc_tot_in_flight(mq) == 1);
|
||||||
cqe_retune_ok = (mmc_cqe_qcnt(mq) == 1);
|
cqe_retune_ok = (mmc_cqe_qcnt(mq) == 1);
|
||||||
@@ -321,6 +328,8 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||||||
bool put_card = false;
|
bool put_card = false;
|
||||||
|
|
||||||
spin_lock_irq(&mq->lock);
|
spin_lock_irq(&mq->lock);
|
||||||
|
if (req_op(req) == REQ_OP_WRITE)
|
||||||
|
mq->pending_writes--;
|
||||||
mq->in_flight[issue_type] -= 1;
|
mq->in_flight[issue_type] -= 1;
|
||||||
if (mmc_tot_in_flight(mq) == 0)
|
if (mmc_tot_in_flight(mq) == 0)
|
||||||
put_card = true;
|
put_card = true;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ struct mmc_queue {
|
|||||||
struct request_queue *queue;
|
struct request_queue *queue;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
int in_flight[MMC_ISSUE_MAX];
|
int in_flight[MMC_ISSUE_MAX];
|
||||||
|
int pending_writes;
|
||||||
unsigned int cqe_busy;
|
unsigned int cqe_busy;
|
||||||
#define MMC_CQE_DCMD_BUSY BIT(0)
|
#define MMC_CQE_DCMD_BUSY BIT(0)
|
||||||
bool busy;
|
bool busy;
|
||||||
|
|||||||
@@ -1122,6 +1122,15 @@ static int sd_parse_ext_reg_perf(struct mmc_card *card, u8 fno, u8 page,
|
|||||||
pr_debug("%s: Command Queue supported depth %u\n",
|
pr_debug("%s: Command Queue supported depth %u\n",
|
||||||
mmc_hostname(card->host),
|
mmc_hostname(card->host),
|
||||||
card->ext_csd.cmdq_depth);
|
card->ext_csd.cmdq_depth);
|
||||||
|
/*
|
||||||
|
* If CQ is enabled, there is a contract between host and card such that
|
||||||
|
* VDD will be maintained and removed only if a power off notification
|
||||||
|
* is provided. An SD card in an accessible slot means surprise removal
|
||||||
|
* is a possibility. As a middle ground, keep the default maximum of 1
|
||||||
|
* posted write unless the card is "hardwired".
|
||||||
|
*/
|
||||||
|
if (!mmc_card_is_removable(card->host))
|
||||||
|
card->max_posted_writes = card->ext_csd.cmdq_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
card->ext_perf.fno = fno;
|
card->ext_perf.fno = fno;
|
||||||
@@ -1385,6 +1394,7 @@ retry:
|
|||||||
|
|
||||||
card->ocr = ocr;
|
card->ocr = ocr;
|
||||||
card->type = MMC_TYPE_SD;
|
card->type = MMC_TYPE_SD;
|
||||||
|
card->max_posted_writes = 1;
|
||||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -380,6 +380,8 @@ struct mmc_card {
|
|||||||
unsigned int nr_parts;
|
unsigned int nr_parts;
|
||||||
|
|
||||||
struct workqueue_struct *complete_wq; /* Private workqueue */
|
struct workqueue_struct *complete_wq; /* Private workqueue */
|
||||||
|
|
||||||
|
unsigned int max_posted_writes; /* command queue posted write limit */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool mmc_large_sector(struct mmc_card *card)
|
static inline bool mmc_large_sector(struct mmc_card *card)
|
||||||
|
|||||||
Reference in New Issue
Block a user