2014年7月30日 星期三

The feature of runtime pm in mmc_get_card& mmc_put_card

今天在porting sandisk 開發的flow,發現有兩個function是目前使用的kernel沒有定義的。
mmc_get_card 以及 mmc_put_card。
google了一下其實就是把原本mmc_claim_host 以及mmc_release_host加上runtime pm相關的function包起來而成。

[PATCH V3 3/4] mmc: block: Enable runtime pm for mmc blkdevice


從這版patch中可以看到mmc_get_card 以及 mmc_put_card的定義。也取代了原本的mmc_claim_host 以及mmc_release_host。


void mmc_get_card(struct mmc_card *card)
{
 pm_runtime_get_sync(&card->dev);
 mmc_claim_host(card->host);
}
EXPORT_SYMBOL(mmc_get_card);

/*
 * This is a helper function, which releases the host and drops the runtime
 * pm reference for the card device.
 */
void mmc_put_card(struct mmc_card *card)
{
 mmc_release_host(card->host);
 pm_runtime_mark_last_busy(&card->dev);
 pm_runtime_put_autosuspend(&card->dev);
}
EXPORT_SYMBOL(mmc_put_card);

從Documentation/power/runtime_pm.txt可以找到這幾個相關function的簡單敘述。




int pm_runtime_get_sync(struct device *dev);
    - increment the device's usage counter, run       
    pm_runtime_resume(dev) and return its result


int pm_runtime_resume(struct device *dev);
    - execute the subsystem-level resume callback for the device
   returns 0 on success, 1 if the device's runtime PM status was already 'active' 
   or error code on failure, where -EAGAIN means it may be safe to attempt to
   resume the device again in future, but 'power.runtime_error' should be checked 
   additionally, and -EACCES means that 'power.disable_depth' is different from 0

void pm_runtime_mark_last_busy(struct device *dev);
    - set the power.last_busy field to the current time

int pm_runtime_put_autosuspend(struct device *dev);
    - decrement the device's usage counter; if the result is 0 then run
    pm_request_autosuspend(dev) and return its result

int pm_request_autosuspend(struct device *dev);
    - schedule the execution of the subsystem-level suspend callback for the
    device when the autosuspend delay has expired; if the delay has already
    expired then the work item is queued up immediately


在文件中有下面這麼一段,這邊想提到的是既然mmc_get_card會引用到pm_runtime_resume,根據文件應該在其他地方會先enable runtime pm.

If the default initial runtime PM status of the device (i.e. 'suspended')
reflects the actual state of the device, its bus type's or its driver's
->probe() callback will likely need to wake it up using one of the PM core's
helper functions described in Section 4.  In that case, pm_runtime_resume()
should be used.  Of course, for this purpose the device's runtime PM has to be
enabled earlier by calling pm_runtime_enable().

再來文件中提到下面這段,也就理解了為何在mmc_put_card呼叫這兩個function。

Inactivity is determined based on the power.last_busy field.  Drivers should
call pm_runtime_mark_last_busy() to update this field after carrying out I/O,
typically just before calling pm_runtime_put_autosuspend(). 

2014年7月15日 星期二

Error: Program “/NDK-build” not found in PATH

Recently, I want to develop android applications with opencv. 
After downloaded opencv sdk and import to my eclipse, Some errors occurred.

One of them is Error: Program “/NDK-build” not found in PATH.

My OS is Ubuntu12.04, it is a linux environment, so i modified the Build command: ${NDKROOT}/ndk-build.cmd to ${NDKROOT}/ndk-build. The reason is that *.cmd is used for Windows version.


Second, in my Environment variables,  I don't have NDKROOT, so I add this variable and set the relative path to my ndk folder. 

As a result, Error: Program “/NDK-build” not found in PATH fixed.



2014年7月13日 星期日

分析eMMC packed command in Linux kernel 之 mmc_blk_prep_packed_list

針對device是否有packed能力做一些篩選條件,若沒有的話會直接goto mmc_blk_clear_packed離開此函式。
分析一、
max_blk_count = min(card->host->max_blk_count,
                        card->host->max_req_size >> 9);
        if (unlikely(max_blk_count > 0xffff))
                max_blk_count = 0xffff;
也就是不管如何host端最多最多一次送的blk_count就是65535blocks.

分析二、
max_phys_segs = queue_max_segments(q);

        req_sectors += blk_rq_sectors(cur);
        phys_segments += cur->nr_phys_segments;

        if (rq_data_dir(cur) == WRITE) {
                req_sectors++;
                phys_segments++;
        }
藉由queue_max_segments得到request_queuemax_segmants值。
藉由blk_rq_sectors得到目前request要傳送sector(512bytes)數目。
/* Number of scatter-gather DMA addr+len pairs after
* physical address coalescing is performed.
*/
unsigned short nr_phys_segments;
藉由nr_phys_segments的註解說明,我們知道紀錄的是scatter-gather DMA addr+len pairs在做完physical address coalescing的數量。
至於physical address coalescing是怎麼樣的行為目前先不探討。
接著如果當前的request是一個寫入要求,req_sectors以及phys_segments各加一。WHY? (直覺想到的是要傳packed commandheader,但是若為packed read也是需要header,目前暫留此懸疑。)
再來進入此函數的重頭戲,
while (reqs < max_packed_rw - 1) {
                spin_lock_irq(q->queue_lock);
                next = blk_fetch_request(q);
                spin_unlock_irq(q->queue_lock);
                if (!next)
                        break;
先利用spin_lock機制鎖住此requestrequest_queue。確保在做blk_fecth_request時的同步問題。
/**
 * blk_fetch_request - fetch a request from a request queue
 * @q: request queue to fetch a request from
 *
 * Description:
 *     Return the request at the top of @q.  The request is started on
 *     return and LLD can start processing it immediately.
 *
 * Return:
 *     Pointer to the request at the top of @q if available.  Null
 *     otherwise.
 *
 * Context:
 *     queue_lock must be held.
 */
struct request *blk_fetch_request(struct request_queue *q)
{
        struct request *rq;

        rq = blk_peek_request(q);
        if (rq)
                blk_start_request(rq);
        return rq;
}
EXPORT_SYMBOL(blk_fetch_request);
分析三、
註解說明其實已經很清楚說明此函數(blk_fetch_request)的任務,這邊我還不了解的是LLD是甚麼? Wiki: LLD(Lower level design, a stage in project management of SW development or other computer-related projects, following the High-level design) 在此LLD是指將此request交給dirver去處理。
分析三-甲、
/**
 * blk_peek_request - peek at the top of a request queue
 * @q: request queue to peek at
 *
 * Description:
 *     Return the request at the top of @q.  The returned request
 *     should be started using blk_start_request() before LLD starts
 *     processing it.
 *
 * Return:
 *     Pointer to the request at the top of @q if available.  Null
 *     otherwise.
 *
 * Context:
 *     queue_lock must be held.
 */
struct request *blk_peek_request(struct request_queue *q)
再度藉由清楚明瞭的註解知道此函數的目的。(良好的註解習慣是優秀程式設計師必備) 另外會再寫一篇詳細分析blk_peek_request跟io-scheduler相關的探討
分析三-乙、
/**
 * blk_start_request - start request processing on the driver
 * @req: request to dequeue
 *
 * Description:
 *     Dequeue @req and start timeout timer on it.  This hands off the
 *     request to the driver.
 *
 *     Block internal functions which don't want to start timer should
 *     call blk_dequeue_request().
 *
 * Context:
 *     queue_lock must be held.
 */
void blk_start_request(struct request *req)
{
        blk_dequeue_request(req);

        /*
         * We are now handing the request to the hardware, initialize
         * resid_len to full count and add the timeout handler.
         */
        req->resid_len = blk_rq_bytes(req);
        if (unlikely(blk_bidi_rq(req)))
                req->next_rq->resid_len = blk_rq_bytes(req->next_rq);

        blk_add_timer(req);
}
EXPORT_SYMBOL(blk_start_request);
在註解中提到若是不想要啟動timeout timer,應該直接呼叫blk_dequeue_request呼叫blk_dequeue_request後,取得目前request要傳送/接收的data長度,並且藉由blk_bidi_rq知道此request後是否接著還有request,接著取得下個request要傳送/接收的data長度,我想這邊是為了提升request收送的效率。
/**
 * blk_add_timer - Start timeout timer for a single request
 * @req: request that is about to start running.
 *
 * Notes:
 *    Each request has its own timer, and as it is added to the queue, we
 *    set up the timer. When the request completes, we cancel the timer.
 */
void blk_add_timer(struct request *req)
這邊就不再多做說明blk_add_timer了。
接著有幾個條件會讓程序走向blk_reqeue_request:
1.      下個requestREQ_DISCARD 或是 REQ_FLUSH
2.      下個requestIO方向不同
3.      下個requestreliable write但是device不支援
4.      加上下個request要求的data block count後,大於host最大的block count
5.      加上下個requestnr_phys_segments後,大於host max_phys_segs
再來,我們看看blk_reqeue_request在做甚麼事。
/**
 * blk_requeue_request - put a request back on queue
 * @q:             request queue where request should be inserted
 * @rq:           request to be inserted
 *
 * Description:
 *    Drivers often keep queueing requests until the hardware cannot accept
 *    more, when that condition happens we need to put the request back
 *    on the queue. Must be called with queue lock held.
 */
void blk_requeue_request(struct request_queue *q, struct request *rq)
因此當hardware還能負荷request或是沒有觸動到上面提到的幾個條件,就會持續呼叫list_add_tailentry加到packed_list中。
/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
        __list_add(new, head->prev, head);
}
離開重頭戲while迴圈後,得知reqs需要執行之數量,呼叫list_add並設定相關packed_num, packed_retries參數後結束整個函式。
/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head, head->next);
}
來看一下,__list_add吧。
static inline void __list_add(struct list_head *new,
                              struct list_head *prev, struct list_head *next)
{
        next->prev = new;   //1
        new->next = next;   //2
        new->prev = prev;   //3
        prev->next = new;   //4
}