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
}



2014年3月12日 星期三

[Record] Embedded Linux Programming Life

Recently, I want to record my working life in the past few months.

Last year, I am a newbie in embedded Linux domain. But I think embedded Linux programming will be popular in the future and I am interested in it. Luckily, my company offers a training course about the Linux driver& application development. This training started my embedded Linux programming life. In this training, I practiced some drivers and application programs with the Tiny6410 platform. 

In the past few months, I have played with the Samsung DK board including the v310, Exynos 5250, Exynos 5410. 
On the v310 platform, I had tried to modify the frequency on the SD/MMC bus by referencing the datasheet and verified it on scope. Implemented customer subsystem and vendor specific flow in the U-boot system.
Exynos 5250 platform is the master platform which I focus on. I modified the specific device driver, debugged, and developed custom modules. During develop android application for customer requirement; I tested three different ways to implement the requirement. First, I modified device driver, developed related source code include HAL, JNI, framework and android app. Second, I implemented another android app by using NDK. In this developing flow, we don't need the HAL, just packed the native code to shared library. The story is not stopped in here. Because it still can't meet the customer's requirement. So, the third way was born. It is used the android runtime to execute Linux application through shell commands. Because I need the root permission in android system for the specific requirement. I have been tested this android runtime app in different platform such as Exynos 5410(Odroid board), MTK 6582M(U969). So, this way is most close to the customer's request. And I am still working on it.

Because my company is focus on developing a specific IC chip, I have the opportunity to develop the related program from device driver to android app. In the other words, it means I need to handle c/c++, java programming language and a little assembly language. In order to analyze some android system behavior, I developed a log analysis program by c#.

If I have time, I would like to share the detail experience about the above topic.