On 11/19/2012 10:48 AM, Konstantin Dorfman wrote: > Hello Per, > > Thank you for giving me an example, below I'm trying to explain some > logic issues of it and asking you some questions about your vision of > the patch. > > On 11/15/2012 06:38 PM, Per Förlin wrote: >> On 11/14/2012 04:15 PM, Konstantin Dorfman wrote: >>> Hello Per, >>> >>> On 11/13/2012 11:10 PM, Per Forlin wrote: >>>> On Tue, Oct 30, 2012 at 1:19 PM, Konstantin Dorfman >>>> <kdorfman@xxxxxxxxxxxxxx> wrote: >>>>> Hello, >>>>> >>>>> On 10/29/2012 11:40 PM, Per Forlin wrote: >>>>>> Hi, >>>>>> >>>>>> I would like to move the focus of my concerns from root cause analysis >>>>>> to the actual patch, >>>>>> My first reflection is that the patch is relatively complex and some >>>>>> of the code looks duplicated. >>>>>> Would it be possible to simplify it and re-use the current execution flow. >>>>>> >>>>>> Is the following flow feasible? >>>>>> >>>>>> in mmc_start_req(): >>>>>> -------------- >>>>>> if (rqc == NULL && not_resend) >>>>>> wait_for_both_mmc_and_arrival_of_new_req >>>>>> else >>>>>> wait_only_for_mmc >>>>>> >>>>>> if (arrival_of_new_req) { >>>>>> set flag to indicate fetch-new_req >>>>>> return NULL; >>>>>> } >>>>>> ----------------- >>>>>> >>>>>> in queue.c: >>>>>> if (fetch-new_req) >>>>>> don't overwrite previous req. >>>>>> >>>>>> BR >>>>>> Per >>>>> >>>>> You are talking about using both waiting mechanisms, old (wait on >>>>> completion) and new - wait_event_interruptible()? But how done() >>>>> callback, called on host controller irq context, will differentiate >>>>> which one is relevant for the request? >>>>> >>>>> I think it is better to use single wait point for mmc thread. >>>> >>>> I have sketch a patch to better explain my point. It's not tested it >>>> barely compiles :) >>>> The patch tries to introduce your feature and still keep the same code >>>> path. And it exposes an API that could be used by SDIO as well. >>>> The intention of my sketch patch is only to explain what I tried to >>>> visualize in the pseudo code previously in this thread. >>>> The out come of your final patch should be documented here I think: >>>> Documentation/mmc/mmc-async-req.txt >>> This document is ready, attaching it to this mail and will be included >>> in next version of the patch (or RESEND). >>>> >>>> Here follows my patch sketch: >>>> ........................................................ >>>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c >>>> index e360a97..08036a1 100644 >>>> --- a/drivers/mmc/card/queue.c >>>> +++ b/drivers/mmc/card/queue.c >>>> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d) >>>> spin_unlock_irq(q->queue_lock); >>>> >>>> if (req || mq->mqrq_prev->req) { >>>> + if (!req) >>>> + mmc_prefetch_start(&mq->mqrq_prev->mmc_active, true); >>>> set_current_state(TASK_RUNNING); >>>> mq->issue_fn(mq, req); >>>> } else { >>>> @@ -79,11 +81,13 @@ static int mmc_queue_thread(void *d) >>>> } >>>> >>>> /* Current request becomes previous request and vice versa. */ >>>> - mq->mqrq_prev->brq.mrq.data = NULL; >>>> - mq->mqrq_prev->req = NULL; >>>> - tmp = mq->mqrq_prev; >>>> - mq->mqrq_prev = mq->mqrq_cur; >>>> - mq->mqrq_cur = tmp; >>>> + if (!mmc_prefetch_pending(&mq->mqrq_prev->mmc_active)) { >>>> + mq->mqrq_prev->brq.mrq.data = NULL; >>>> + mq->mqrq_prev->req = NULL; >>>> + tmp = mq->mqrq_prev; >>>> + mq->mqrq_prev = mq->mqrq_cur; >>>> + mq->mqrq_cur = tmp; >>>> + } >>>> } while (1); >>>> up(&mq->thread_sem); >>>> >>>> @@ -109,10 +113,44 @@ static void mmc_request_fn(struct request_queue *q) >>>> return; >>>> } >>>> >>>> + if (mq->prefetch_enable) { >>>> + spin_lock(&mq->prefetch_lock); >>>> + if (mq->prefetch_completion) >>>> + complete(mq->prefetch_completion); > Since mq->prefetch_completion init happens only in core.c after > mmc_start_req(NULL), we can miss all new request notifications coming > from fetching NULL request until then. It's a mistake in the patch. I meant check mq->prefetch_pending to know whether we need to wait in the first place. That's why mq->prefetch_pending is assigned even if mq->prefetch_completion is not set yet. Patch needs to be updated to take it into account. One could let mmc_prefecth_init() return and indicate if there is already a NEW_REQ pending. If this is the case skip wait. >>>> + mq->prefetch_pending = true; >>>> + spin_unlock(&mq->prefetch_lock); >>>> + } >>>> + >>>> if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) >>>> wake_up_process(mq->thread); >>>> } >>>> >>>> +static void mmc_req_init(struct mmc_async_req *areq, struct completion *compl) >>>> +{ >>>> + struct mmc_queue *mq = >>>> + container_of(areq->prefetch, struct mmc_queue, prefetch); >>>> + >>>> + spin_lock(&mq->prefetch_lock); >>>> + mq->prefetch_completion = compl; >>>> + if (mq->prefetch_pending) >>>> + complete(mq->prefetch_completion); >>>> + spin_unlock(&mq->prefetch_lock); >>>> +} >>>> + >>>> +static void mmc_req_start(struct mmc_async_req *areq, bool enable) >>>> +{ >>>> + struct mmc_queue *mq = >>>> + container_of(areq->prefetch, struct mmc_queue, prefetch); >>>> + mq->prefetch_enable = enable; >>>> +} >>>> + >>>> +static bool mmc_req_pending(struct mmc_async_req *areq) >>>> +{ >>>> + struct mmc_queue *mq = >>>> + container_of(areq->prefetch, struct mmc_queue, prefetch); >>>> + return mq->prefetch_pending; >>>> +} >>>> + >>>> static struct scatterlist *mmc_alloc_sg(int sg_len, int *err) >>>> { >>>> struct scatterlist *sg; >>>> @@ -166,6 +204,12 @@ int mmc_init_queue(struct mmc_queue *mq, struct >>>> mmc_card *card, >>>> int ret; >>>> struct mmc_queue_req *mqrq_cur = &mq->mqrq[0]; >>>> struct mmc_queue_req *mqrq_prev = &mq->mqrq[1]; >>>> + spin_lock_init(&mq->prefetch_lock); >>>> + mq->prefetch.wait_req_init = mmc_req_init; >>>> + mq->prefetch.wait_req_start = mmc_req_start; >>>> + mq->prefetch.wait_req_pending = mmc_req_pending; >>>> + mqrq_cur->mmc_active.prefetch = &mq->prefetch; >>>> + mqrq_prev->mmc_active.prefetch = &mq->prefetch; >>>> >>>> if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) >>>> limit = *mmc_dev(host)->dma_mask; >>>> diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h >>>> index d2a1eb4..5afd467 100644 >>>> --- a/drivers/mmc/card/queue.h >>>> +++ b/drivers/mmc/card/queue.h >>>> @@ -33,6 +33,12 @@ struct mmc_queue { >>>> struct mmc_queue_req mqrq[2]; >>>> struct mmc_queue_req *mqrq_cur; >>>> struct mmc_queue_req *mqrq_prev; >>>> + >>>> + struct mmc_async_prefetch prefetch; >>>> + spinlock_t prefetch_lock; >>>> + struct completion *prefetch_completion; >>>> + bool prefetch_enable; >>>> + bool prefetch_pending; >>>> }; >>>> >>>> extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *, >>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c >>>> index 9503cab..06fc036 100644 >>>> --- a/drivers/mmc/core/core.c >>>> +++ b/drivers/mmc/core/core.c >>>> @@ -352,7 +352,15 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, >>>> mmc_pre_req(host, areq->mrq, !host->areq); >>>> >>>> if (host->areq) { >>>> + if (!areq) >>>> + mmc_prefetch_init(host->areq, >>>> + &host->areq->mrq->completion); >>>> mmc_wait_for_req_done(host, host->areq->mrq); >>>> + if (!areq) { >>>> + mmc_prefetch_start(host->areq, false); >>>> + if (mmc_prefetch_pending(host->areq)) >>>> + return NULL; > In this case, mmc thread may be unblocked because done() arrived for > current request and not because new request notification. In such a case > we would like the done request to be handled before fetching the new > request. I agree. We wake up and there is no pending new request execution should proceed normally by handling the completed request. > In my code is_done_rcv flag used along with is_new_req flag in > order to differentiate the reason for mmc thread awake. In my patch the return value of "mmc_prefetch_pending()" specifies if thee is a pending request.what should happen. If both current request is done and mmc_prefetch_pending is done at the same time I see there is a problem with my patch. There needs to be a flag to indicate if current request is done. > >>>> + } >>>> err = host->areq->err_check(host->card, host->areq); >>>> } >>>> >>>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h >>>> index 65c64ee..ce5d03f 100644 >>>> --- a/include/linux/mmc/host.h >>>> +++ b/include/linux/mmc/host.h >>>> @@ -15,6 +15,7 @@ >>>> #include <linux/sched.h> >>>> #include <linux/device.h> >>>> #include <linux/fault-inject.h> >>>> +#include <linux/completion.h> >>>> >>>> #include <linux/mmc/core.h> >>>> #include <linux/mmc/pm.h> >>>> @@ -140,6 +141,13 @@ struct mmc_host_ops { >>>> >>>> struct mmc_card; >>>> struct device; >>>> +struct mmc_async_req; >>>> + >>>> +struct mmc_async_prefetch { >>>> + void (*wait_req_init)(struct mmc_async_req *, struct completion *); >>>> + void (*wait_req_start)(struct mmc_async_req *, bool); >>>> + bool (*wait_req_pending)(struct mmc_async_req *); >>>> +}; >>>> >>>> struct mmc_async_req { >>>> /* active mmc request */ >>>> @@ -149,8 +157,33 @@ struct mmc_async_req { >>>> * Returns 0 if success otherwise non zero. >>>> */ >>>> int (*err_check) (struct mmc_card *, struct mmc_async_req *); >>>> + struct mmc_async_prefetch *prefetch; >>>> }; >>>> >>>> +/* set completion variable, complete == NULL to reset completion */ >>>> +static inline void mmc_prefetch_init(struct mmc_async_req *areq, >>>> + struct completion *complete) >>>> +{ >>>> + if (areq->prefetch && areq->prefetch->wait_req_init) >>>> + areq->prefetch->wait_req_init(areq, complete); >>>> +} >>>> + >>>> +/* enable or disable prefetch feature */ >>>> +static inline void mmc_prefetch_start(struct mmc_async_req *areq, bool enable) >>>> +{ >>>> + if (areq->prefetch && areq->prefetch->wait_req_start) >>>> + areq->prefetch->wait_req_start(areq, enable); >>>> +} >>>> + >>>> +/* return true if new request is pending otherwise false */ >>>> +static inline bool mmc_prefetch_pending(struct mmc_async_req *areq) >>>> +{ >>>> + if (areq->prefetch && areq->prefetch->wait_req_pending) >>>> + return areq->prefetch->wait_req_pending(areq); >>>> + else >>>> + return false; >>>> +} >>>> + >>>> /** >>>> * struct mmc_slot - MMC slot functions >>>> * >>>> .................................................................... >>>> >>>> >>>> BR >>>> Per >>> I understand your motivation and idea for re-structure the patch. It is >>> still not clear for me how exactly mmc thread will be awaken on new >>> request notification in your version, but I have another problem: >>> >> mmc_request_fn() is called and it calls complete(mq->prefetch_completion) which wakes up the current thread. >> My patch is just an example. The intention is to make the patch cleaner. But I may have missed some of the HPI aspects. > > Is it the lack of functions wrappers that you are using in your example? It's fine to skip the functions wrappers if it makes no sense. What I want to avoid is to create new functions for data_req_start and data_req_wait. I would rather add this to the existing functions in core.c and keep changes in block.c and the flow minimal. > As I understand your example, you mean to implement generic logic on > core/core.c level by using wrapper functions and leave final > implementation for MMC to card/queue.c and for SDIO layer to > card/..sdio.. (I'm not too familiar with sdio protocol implementation). > Well, it is make a lot of sense. > I still have "plans" to add pre/post/async support in the SDIO framework too but I have not got to it. I would be nice to add your NEW_REQ feature along with the other mmc-async features, to make it usable from SDIO. > But the devil is in details - there is a lot of work in > mmc_wait_for_data_req_done(), done() callback and also error handling > changes for card/block.c Things in core.c could be re-used in the SDIO case. In block.c there are only FS specific implementation not relevant for SDIO. > > Do you think, that wait_event() API used not suits the same semantic as > completion API? The waiting mechanims may be wait_event or completion. I'm not in favor of using both. Better to replace completion with wait_event if you prefer. My main concern is to avoid adding new API to core.c in order to add the NEW_REQ feature. My patch is an example of changes to be done in core.c without adding new functions. > > We would like to have a generic capability to handle additional events, > such as HPI/stop flow, in addition to the NEW_REQUEST notification. > Therefore, an event mechanism seems to be a better design than completion. > > I've looked at SDIO code and from what I can understand, right now SDIO > is not using async request mechanism and works from 'wait_for_cmd()` > level. This means that such work as exposing MMC core API's is major > change and definitely should be separate design and implementation > effort, while my current patch right now will fix mmc thread behavior > and give better performance for upper layers. You are right. There is no support in the SDIO implementation for pre/post/async feature. Still I had it in mind design the "struct mmc_async". It's possible to re-use the mmc core parts in order to utilize this support in the SDIO case. I'm not saying we should design for SDIO but at least keep the API in a way that it's potential usable from SDIO too. The API of core.c (start/wait of requests) should make sense without the existence of MMC block driver (block/queue). One way to test the design is to add a test in mmc_test.c for penging NEW_REQ. In mmc_test.c there is not block.c or queue.c. If the test case if simple to write in mmc_test.c it's means that the API is on a good level. BR Per > > >> >> If the API is not implemented the mmc core shall simply ignore it. >> >>> We want to expand this event based mechanism (unblock mmc thread from >>> waiting for the running request) by adding another reason/event. This is >>> URGENT_REQUEST event. When block layer has Urgent request to execute, it >>> notifies mmc layer (similar to mmc_request() notification) and mmc layer >>> handles this urgent request notification by correctly stopping running >>> request, re-inserting back to I/O scheduler all not yet performed parts >>> and fetching & starting the urgent request. >>> The reasoning and general idea are documented together with "New event" >>> and will be submitted soon. The patch itself will be submitted in a week >>> or so. >> I have not consider use of HPI in my proposal. If the NEW_REQ is first in line in the mmc queue it will be fetched as the NEW_REQ. >> Then the current request must be interrupted and returned to mmc-queue or io-scheduler. >> >> I don't see a direct contradiction between the two designs. >> >> The main point is to make the NEW_REQ API more simple clear. >> My example is just an example. >> >>> >>> As for our discussion - to handle both events mmc layer should be >>> capable to be awaken not only in case of no mmc_prefetch_pending() (in >>> your patch terms), but also when we have perfect case of async requests: >>> one running on the bus and second prepared and waiting for the first. >>> >>> I think, that in case SDIO protocol driver need such functionality as >>> NEW_REQUEST, one need to implement notification to the mmc layer similar >>> to mmc_request() code in my patch. >> SDIO needs to implement the new NEW_REQ API, but the API needs to be clean. >> >> BR >> Per >> > Best regards, > -- > Konstantin Dorfman, > QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, > Inc. is a member of Code Aurora Forum, > hosted by The Linux Foundation > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html