summaryrefslogtreecommitdiff
path: root/drivers/nvme/host/apple.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nvme/host/apple.c')
-rw-r--r--drivers/nvme/host/apple.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index ff8b083dc5c6..4481ffc97993 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -195,8 +195,20 @@ struct apple_nvme {
int irq;
spinlock_t lock;
+
+ /*
+ * Delayed cache flush handling state
+ */
+ struct nvme_ns *flush_ns;
+ unsigned long flush_interval;
+ unsigned long last_flush;
+ struct delayed_work flush_dwork;
};
+unsigned int flush_interval = 1000;
+module_param(flush_interval, uint, 0644);
+MODULE_PARM_DESC(flush_interval, "Grace period in msecs between flushes");
+
static_assert(sizeof(struct nvme_command) == 64);
static_assert(sizeof(struct apple_nvmmu_tcb) == 128);
@@ -729,6 +741,26 @@ static int apple_nvme_remove_sq(struct apple_nvme *anv)
return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0);
}
+static bool apple_nvme_delayed_flush(struct apple_nvme *anv, struct nvme_ns *ns,
+ struct request *req)
+{
+ if (!anv->flush_interval || req_op(req) != REQ_OP_FLUSH)
+ return false;
+ if (delayed_work_pending(&anv->flush_dwork))
+ return true;
+ if (time_before(jiffies, anv->last_flush + anv->flush_interval)) {
+ kblockd_mod_delayed_work_on(WORK_CPU_UNBOUND, &anv->flush_dwork,
+ anv->flush_interval);
+ if (WARN_ON_ONCE(anv->flush_ns && anv->flush_ns != ns))
+ goto out;
+ anv->flush_ns = ns;
+ return true;
+ }
+out:
+ anv->last_flush = jiffies;
+ return false;
+}
+
static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
@@ -764,6 +796,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
}
blk_mq_start_request(req);
+
+ if (apple_nvme_delayed_flush(anv, ns, req)) {
+ blk_mq_complete_request(req);
+ return BLK_STS_OK;
+ }
+
apple_nvme_submit_cmd(q, cmnd);
return BLK_STS_OK;
@@ -1373,6 +1411,28 @@ static void devm_apple_nvme_mempool_destroy(void *data)
mempool_destroy(data);
}
+static void apple_nvme_flush_work(struct work_struct *work)
+{
+ struct nvme_command c = { };
+ struct apple_nvme *anv;
+ struct nvme_ns *ns;
+ int err;
+
+ anv = container_of(work, struct apple_nvme, flush_dwork.work);
+ ns = anv->flush_ns;
+ if (WARN_ON_ONCE(!ns))
+ return;
+
+ c.common.opcode = nvme_cmd_flush;
+ c.common.nsid = cpu_to_le32(anv->flush_ns->head->ns_id);
+ err = nvme_submit_sync_cmd(ns->queue, &c, NULL, 0);
+ if (err) {
+ dev_err(anv->dev, "Deferred flush failed: %d\n", err);
+ } else {
+ anv->last_flush = jiffies;
+ }
+}
+
static int apple_nvme_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1515,12 +1575,21 @@ static int apple_nvme_probe(struct platform_device *pdev)
goto put_dev;
}
+ if (flush_interval) {
+ anv->flush_interval = msecs_to_jiffies(flush_interval);
+ anv->flush_ns = NULL;
+ anv->last_flush = jiffies - anv->flush_interval;
+ }
+
+ INIT_DELAYED_WORK(&anv->flush_dwork, apple_nvme_flush_work);
+
nvme_reset_ctrl(&anv->ctrl);
async_schedule(apple_nvme_async_probe, anv);
return 0;
put_dev:
+ apple_nvme_detach_genpd(anv);
put_device(anv->dev);
return ret;
}
@@ -1548,6 +1617,7 @@ static void apple_nvme_shutdown(struct platform_device *pdev)
{
struct apple_nvme *anv = platform_get_drvdata(pdev);
+ flush_delayed_work(&anv->flush_dwork);
apple_nvme_disable(anv, true);
if (apple_rtkit_is_running(anv->rtk))
apple_rtkit_shutdown(anv->rtk);