cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mmc_hsq.c (7939B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *
      4 * MMC software queue support based on command queue interfaces
      5 *
      6 * Copyright (C) 2019 Linaro, Inc.
      7 * Author: Baolin Wang <baolin.wang@linaro.org>
      8 */
      9
     10#include <linux/mmc/card.h>
     11#include <linux/mmc/host.h>
     12#include <linux/module.h>
     13
     14#include "mmc_hsq.h"
     15
     16#define HSQ_NUM_SLOTS	64
     17#define HSQ_INVALID_TAG	HSQ_NUM_SLOTS
     18
     19static void mmc_hsq_retry_handler(struct work_struct *work)
     20{
     21	struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work);
     22	struct mmc_host *mmc = hsq->mmc;
     23
     24	mmc->ops->request(mmc, hsq->mrq);
     25}
     26
     27static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
     28{
     29	struct mmc_host *mmc = hsq->mmc;
     30	struct hsq_slot *slot;
     31	unsigned long flags;
     32	int ret = 0;
     33
     34	spin_lock_irqsave(&hsq->lock, flags);
     35
     36	/* Make sure we are not already running a request now */
     37	if (hsq->mrq) {
     38		spin_unlock_irqrestore(&hsq->lock, flags);
     39		return;
     40	}
     41
     42	/* Make sure there are remain requests need to pump */
     43	if (!hsq->qcnt || !hsq->enabled) {
     44		spin_unlock_irqrestore(&hsq->lock, flags);
     45		return;
     46	}
     47
     48	slot = &hsq->slot[hsq->next_tag];
     49	hsq->mrq = slot->mrq;
     50	hsq->qcnt--;
     51
     52	spin_unlock_irqrestore(&hsq->lock, flags);
     53
     54	if (mmc->ops->request_atomic)
     55		ret = mmc->ops->request_atomic(mmc, hsq->mrq);
     56	else
     57		mmc->ops->request(mmc, hsq->mrq);
     58
     59	/*
     60	 * If returning BUSY from request_atomic(), which means the card
     61	 * may be busy now, and we should change to non-atomic context to
     62	 * try again for this unusual case, to avoid time-consuming operations
     63	 * in the atomic context.
     64	 *
     65	 * Note: we just give a warning for other error cases, since the host
     66	 * driver will handle them.
     67	 */
     68	if (ret == -EBUSY)
     69		schedule_work(&hsq->retry_work);
     70	else
     71		WARN_ON_ONCE(ret);
     72}
     73
     74static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
     75{
     76	struct hsq_slot *slot;
     77	int tag;
     78
     79	/*
     80	 * If there are no remain requests in software queue, then set a invalid
     81	 * tag.
     82	 */
     83	if (!remains) {
     84		hsq->next_tag = HSQ_INVALID_TAG;
     85		return;
     86	}
     87
     88	/*
     89	 * Increasing the next tag and check if the corresponding request is
     90	 * available, if yes, then we found a candidate request.
     91	 */
     92	if (++hsq->next_tag != HSQ_INVALID_TAG) {
     93		slot = &hsq->slot[hsq->next_tag];
     94		if (slot->mrq)
     95			return;
     96	}
     97
     98	/* Othersie we should iterate all slots to find a available tag. */
     99	for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) {
    100		slot = &hsq->slot[tag];
    101		if (slot->mrq)
    102			break;
    103	}
    104
    105	if (tag == HSQ_NUM_SLOTS)
    106		tag = HSQ_INVALID_TAG;
    107
    108	hsq->next_tag = tag;
    109}
    110
    111static void mmc_hsq_post_request(struct mmc_hsq *hsq)
    112{
    113	unsigned long flags;
    114	int remains;
    115
    116	spin_lock_irqsave(&hsq->lock, flags);
    117
    118	remains = hsq->qcnt;
    119	hsq->mrq = NULL;
    120
    121	/* Update the next available tag to be queued. */
    122	mmc_hsq_update_next_tag(hsq, remains);
    123
    124	if (hsq->waiting_for_idle && !remains) {
    125		hsq->waiting_for_idle = false;
    126		wake_up(&hsq->wait_queue);
    127	}
    128
    129	/* Do not pump new request in recovery mode. */
    130	if (hsq->recovery_halt) {
    131		spin_unlock_irqrestore(&hsq->lock, flags);
    132		return;
    133	}
    134
    135	spin_unlock_irqrestore(&hsq->lock, flags);
    136
    137	 /*
    138	  * Try to pump new request to host controller as fast as possible,
    139	  * after completing previous request.
    140	  */
    141	if (remains > 0)
    142		mmc_hsq_pump_requests(hsq);
    143}
    144
    145/**
    146 * mmc_hsq_finalize_request - finalize one request if the request is done
    147 * @mmc: the host controller
    148 * @mrq: the request need to be finalized
    149 *
    150 * Return true if we finalized the corresponding request in software queue,
    151 * otherwise return false.
    152 */
    153bool mmc_hsq_finalize_request(struct mmc_host *mmc, struct mmc_request *mrq)
    154{
    155	struct mmc_hsq *hsq = mmc->cqe_private;
    156	unsigned long flags;
    157
    158	spin_lock_irqsave(&hsq->lock, flags);
    159
    160	if (!hsq->enabled || !hsq->mrq || hsq->mrq != mrq) {
    161		spin_unlock_irqrestore(&hsq->lock, flags);
    162		return false;
    163	}
    164
    165	/*
    166	 * Clear current completed slot request to make a room for new request.
    167	 */
    168	hsq->slot[hsq->next_tag].mrq = NULL;
    169
    170	spin_unlock_irqrestore(&hsq->lock, flags);
    171
    172	mmc_cqe_request_done(mmc, hsq->mrq);
    173
    174	mmc_hsq_post_request(hsq);
    175
    176	return true;
    177}
    178EXPORT_SYMBOL_GPL(mmc_hsq_finalize_request);
    179
    180static void mmc_hsq_recovery_start(struct mmc_host *mmc)
    181{
    182	struct mmc_hsq *hsq = mmc->cqe_private;
    183	unsigned long flags;
    184
    185	spin_lock_irqsave(&hsq->lock, flags);
    186
    187	hsq->recovery_halt = true;
    188
    189	spin_unlock_irqrestore(&hsq->lock, flags);
    190}
    191
    192static void mmc_hsq_recovery_finish(struct mmc_host *mmc)
    193{
    194	struct mmc_hsq *hsq = mmc->cqe_private;
    195	int remains;
    196
    197	spin_lock_irq(&hsq->lock);
    198
    199	hsq->recovery_halt = false;
    200	remains = hsq->qcnt;
    201
    202	spin_unlock_irq(&hsq->lock);
    203
    204	/*
    205	 * Try to pump new request if there are request pending in software
    206	 * queue after finishing recovery.
    207	 */
    208	if (remains > 0)
    209		mmc_hsq_pump_requests(hsq);
    210}
    211
    212static int mmc_hsq_request(struct mmc_host *mmc, struct mmc_request *mrq)
    213{
    214	struct mmc_hsq *hsq = mmc->cqe_private;
    215	int tag = mrq->tag;
    216
    217	spin_lock_irq(&hsq->lock);
    218
    219	if (!hsq->enabled) {
    220		spin_unlock_irq(&hsq->lock);
    221		return -ESHUTDOWN;
    222	}
    223
    224	/* Do not queue any new requests in recovery mode. */
    225	if (hsq->recovery_halt) {
    226		spin_unlock_irq(&hsq->lock);
    227		return -EBUSY;
    228	}
    229
    230	hsq->slot[tag].mrq = mrq;
    231
    232	/*
    233	 * Set the next tag as current request tag if no available
    234	 * next tag.
    235	 */
    236	if (hsq->next_tag == HSQ_INVALID_TAG)
    237		hsq->next_tag = tag;
    238
    239	hsq->qcnt++;
    240
    241	spin_unlock_irq(&hsq->lock);
    242
    243	mmc_hsq_pump_requests(hsq);
    244
    245	return 0;
    246}
    247
    248static void mmc_hsq_post_req(struct mmc_host *mmc, struct mmc_request *mrq)
    249{
    250	if (mmc->ops->post_req)
    251		mmc->ops->post_req(mmc, mrq, 0);
    252}
    253
    254static bool mmc_hsq_queue_is_idle(struct mmc_hsq *hsq, int *ret)
    255{
    256	bool is_idle;
    257
    258	spin_lock_irq(&hsq->lock);
    259
    260	is_idle = (!hsq->mrq && !hsq->qcnt) ||
    261		hsq->recovery_halt;
    262
    263	*ret = hsq->recovery_halt ? -EBUSY : 0;
    264	hsq->waiting_for_idle = !is_idle;
    265
    266	spin_unlock_irq(&hsq->lock);
    267
    268	return is_idle;
    269}
    270
    271static int mmc_hsq_wait_for_idle(struct mmc_host *mmc)
    272{
    273	struct mmc_hsq *hsq = mmc->cqe_private;
    274	int ret;
    275
    276	wait_event(hsq->wait_queue,
    277		   mmc_hsq_queue_is_idle(hsq, &ret));
    278
    279	return ret;
    280}
    281
    282static void mmc_hsq_disable(struct mmc_host *mmc)
    283{
    284	struct mmc_hsq *hsq = mmc->cqe_private;
    285	u32 timeout = 500;
    286	int ret;
    287
    288	spin_lock_irq(&hsq->lock);
    289
    290	if (!hsq->enabled) {
    291		spin_unlock_irq(&hsq->lock);
    292		return;
    293	}
    294
    295	spin_unlock_irq(&hsq->lock);
    296
    297	ret = wait_event_timeout(hsq->wait_queue,
    298				 mmc_hsq_queue_is_idle(hsq, &ret),
    299				 msecs_to_jiffies(timeout));
    300	if (ret == 0) {
    301		pr_warn("could not stop mmc software queue\n");
    302		return;
    303	}
    304
    305	spin_lock_irq(&hsq->lock);
    306
    307	hsq->enabled = false;
    308
    309	spin_unlock_irq(&hsq->lock);
    310}
    311
    312static int mmc_hsq_enable(struct mmc_host *mmc, struct mmc_card *card)
    313{
    314	struct mmc_hsq *hsq = mmc->cqe_private;
    315
    316	spin_lock_irq(&hsq->lock);
    317
    318	if (hsq->enabled) {
    319		spin_unlock_irq(&hsq->lock);
    320		return -EBUSY;
    321	}
    322
    323	hsq->enabled = true;
    324
    325	spin_unlock_irq(&hsq->lock);
    326
    327	return 0;
    328}
    329
    330static const struct mmc_cqe_ops mmc_hsq_ops = {
    331	.cqe_enable = mmc_hsq_enable,
    332	.cqe_disable = mmc_hsq_disable,
    333	.cqe_request = mmc_hsq_request,
    334	.cqe_post_req = mmc_hsq_post_req,
    335	.cqe_wait_for_idle = mmc_hsq_wait_for_idle,
    336	.cqe_recovery_start = mmc_hsq_recovery_start,
    337	.cqe_recovery_finish = mmc_hsq_recovery_finish,
    338};
    339
    340int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
    341{
    342	hsq->num_slots = HSQ_NUM_SLOTS;
    343	hsq->next_tag = HSQ_INVALID_TAG;
    344
    345	hsq->slot = devm_kcalloc(mmc_dev(mmc), hsq->num_slots,
    346				 sizeof(struct hsq_slot), GFP_KERNEL);
    347	if (!hsq->slot)
    348		return -ENOMEM;
    349
    350	hsq->mmc = mmc;
    351	hsq->mmc->cqe_private = hsq;
    352	mmc->cqe_ops = &mmc_hsq_ops;
    353
    354	INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
    355	spin_lock_init(&hsq->lock);
    356	init_waitqueue_head(&hsq->wait_queue);
    357
    358	return 0;
    359}
    360EXPORT_SYMBOL_GPL(mmc_hsq_init);
    361
    362void mmc_hsq_suspend(struct mmc_host *mmc)
    363{
    364	mmc_hsq_disable(mmc);
    365}
    366EXPORT_SYMBOL_GPL(mmc_hsq_suspend);
    367
    368int mmc_hsq_resume(struct mmc_host *mmc)
    369{
    370	return mmc_hsq_enable(mmc, NULL);
    371}
    372EXPORT_SYMBOL_GPL(mmc_hsq_resume);
    373
    374MODULE_DESCRIPTION("MMC Host Software Queue support");
    375MODULE_LICENSE("GPL v2");