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.

2013年9月28日 星期六

[U-boot] analysis md for disscuss volatile and pointer

健忘如我,只好常常記錄讀過的東西來加深印象。
本記錄藉由 md (memory display) 來探討 volatile 以及 pointer 間接取值

我們在使用md時,實際上是呼叫do_mem_md函式。

在 do_mem_md 函式中 有三個case最後都會call print_buffer(... 秀出資料
1. print_buffer(addr, p, size, linebytes/size, DISP_LINE_LEN/size);
2. print_buffer(addr, linebuf, size, linebytes/size, DISP_LINE_LEN/size);
3. print_buffer(addr, (void*)addr, size, length, DISP_LINE_LEN/size);
這裡以case 3 來作探討: 要印的資料就是addr的內容,在此只說明如何印出一筆資料。

先來看看print_buffer的宣告
int print_buffer (ulong addr, void* data, uint width, uint count, uint linelen);

就是因為函式的data參數型態為void*,因此須要先將addr轉換為void*的型態。

在print_buffer中和data相關的說明擷取如下

uint32_t x;
/* Copy from memory into linebuf and print hex values */
x = lb.ui[i] = *(volatile uint32_t *)data;
printf(" %0*x", width * 2, x);

由於傳入的實際上是記憶體中的位址addr,而要印的資料型態為uint32_t,因此要先做轉型 (uint32_t *) 至於volatile  稍後會說明。 再藉由間接取值的方式將記憶體內容assign給 x 作輸出。

再來說明volatile,網路上已經很多優秀的說明,這邊列出我覺得不錯的一個連結
http://blog.csdn.net/c_bg44/article/details/1538235

用volatile 的變數每次被呼叫使用時,會從ram中取值。
而沒有使用volatile的變數有可能從cpu register中取值,這是因為此變數之前有被呼叫使用過。(編譯器優化程式碼的結果,因為從cpu register取值比ram快多了)

因此若想要確保每次的取值都是乖乖從ram拿出來就要加上volatile。







2013年7月23日 星期二

[Linux] inode

 inode 译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件。 
      做个比喻,比如一本书,存储设备或分区就相当于这本书,Block相当于书中的每一页,inode 就相当于这本书前面的目录,一本书有很多的内容,如果想查找某部份的内容,我们可以先查目录,通过目录能最快的找到我们想要看的内容。
      当我们用ls 查看某个目录或文件时,如果加上-i 参数,就可以看到inode节点了;比如ls -li lsfile.sh ,最前面的数值就是inode信息

用f2fs的實例來看,發現新的檔案建立後,並不會連續的使用inode節點。







Refs
[1] http://www.cnblogs.com/QJohnson/archive/2011/06/24/2089414.html

[Linux] kmem_cache

今天對於 f2fs 作GC時,宣告的第一個structure來做探討
static struct kmem_cache *winode_slab;
稍微追了一下winode_slab出現的地方。

先來介紹一下大神上搜到的資訊 在Refs[1]中提到:

在内核编程中,可能经常会有一些数据结构需要反复使用和释放,按照通常的思路,可能是使用kmallockfree来实现。
但是这种方式效率不高,Linux为我们提供了更加高效的方法——Slab高速缓存管理器
通过先使用kmem_cache_create函数创建一个高速缓存的头指针——在内核中是struct kmem_cache结构 (這不就是在f2fs裡面宣告的結構嗎!!),具体用法可以这样:
struct kmem_cache *cachep = NULL;
cachep = kmem_cache_create("cache_name", sizeof(struct yourstruct), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);

 (應該可以在f2fs中也找到類似的東東!!)
果不其然~找到啦~

这样我们就获得了一个可用的cachep头指针。 (在f2fs的話就是 winode_slab啦)

当需要分配一个struct yourstruct的结构体空间时,我们只需要调用kmem_cache_alloc函数,就可以获得一个足够我们使用的空间的指针(为什么我要说足够呢?因为刚才的声明中我使用了一个标志——SLAB_HWCACHE_ALIGN,这个标志会让分配的空间对于硬件来说是对齐的,而不一定恰好等于sizeof(struct yourstruct)的结果)。范例代码如下:
struct yourstruct *bodyp = NULL;

bodyp = (struct yourstruct *) kmem_cache_alloc(cachep, GFP_ATOMIC & ~__GFP_DMA);

这样就可以使用bodyp指针所对应的空间存贮你需要的结构体信息了。
当用完结束后,我们需要释放空间,如何操作呢?代码很简单:
kmem_cache_free(cachep, bodyp);

在這邊我們注意到 f2fs中使用的flag 是 SLAB_RECLAIM_ACCOUNT 並非 SLAB_HWCACHE_ALIGN
 在Refs[2],[3]中提到: 
SLAB_RECLAIM_ACCOUNT:内核检查用户态程序有没有足够内存可用,被标记为这个标志的Slab将作为通缉对象。
SLAB_RECLAIM_ACCOUNT flag set, the page frames assigned to the slabs are accounted for as reclaimable pages when the kernel checks whether there is enough memory to satisfy some User Mode requests.


(應該可以在f2fs中也找到類似的東東!!)
果不其然~又找到啦~
  







ps. 若整個cache都不需要時,在f2fs中是call kmem_cache_destory~


kmem_cache_alloc()中第二個參數的探討,在Refs[4]中提到:

GFP_ATOMIC
用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
GFP_KERNEL
内核内存的正常分配. 可能睡眠.
GFP_USER
用来为用户空间页来分配内存; 它可能睡眠.
GFP_HIGHUSER
如同 GFP_USER, 但是从高端内存分配, 如果有. 高端内存在下一个子节描述.
GFP_NOIO
GFP_NOFS
这个标志功能如同 GFP_KERNEL, 但是它们增加限制到内核能做的来满足请求. 一个 GFP_NOFS 分配不允许进行任何文件系统调用, 而 GFP_NOIO 根本不允许任何 I/O 初始化. 它们主要地用在文件系统和虚拟内存代码, 那里允许一个分配睡眠, 但是递归的文件系统调用会是一个坏注意.
上面列出的这些分配标志可以是下列标志的相或来作为参数, 这些标志改变这些分配如何进行:
__GFP_DMA
这个标志要求分配在能够 DMA 的内存区. 确切的含义是平台依赖的并且在下面章节来解释.
__GFP_HIGHMEM
这个标志指示分配的内存可以位于高端内存.
__GFP_COLD
正常地, 内存分配器尽力返回"缓冲热"的页 -- 可能在处理器缓冲中找到的页. 相反, 这个标志请求一个"冷"页, 它在一段时间没被使用. 它对分配页作 DMA 读是有用的, 此时在处理器缓冲中出现是无用的. 一个完整的对如何分配 DMA 缓存的讨论看"直接内存存取"一节在第 1 章.
__GFP_NOWARN
这个很少用到的标志阻止内核来发出警告(使用 printk ), 当一个分配无法满足.
__GFP_HIGH
这个标志标识了一个高优先级请求, 它被允许来消耗甚至被内核保留给紧急状况的最后的内存页.
__GFP_REPEAT
__GFP_NOFAIL
__GFP_NORETRY
这些标志修改分配器如何动作, 当它有困难满足一个分配. __GFP_REPEAT 意思是" 更尽力些尝试" 通过重复尝试 -- 但是分配可能仍然失败. __GFP_NOFAIL 标志告诉分配器不要失败; 它尽最大努力来满足要求. 使用 __GFP_NOFAIL 是强烈不推荐的; 可能从不会有有效的理由在一个设备驱动中使用它. 最后, __GFP_NORETRY 告知分配器立即放弃如果得不到请求的内存.

Refs:
[1] http://blog.csdn.net/leolinux/article/details/6103712
[2] http://blog.csdn.net/tq02h2a/article/details/3628402
[3] http://linux.chinaunix.net/techdoc/net/2008/03/29/987845.shtml
[4] http://oss.org.cn/kernel-book/ldd3/ch08.html