20210928 我的第一次总结
这个接口是用来保证中间执行的代码不被打断的。
20211005 我的第二次总结 把host加入到等待队列中,
20211007
我的第三次理解:用sdio_claim_host和sdio_release_host是为了以独占的方式访问SDIO的host
void sdio_claim_host(struct sdio_func *func)
{
mmc_claim_host(func->card->host);
}
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);
}
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
might_sleep();
add_wait_queue(&host->wq, &wait);
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
if (!stop) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
if (!stop)
mmc_host_enable(host);
return stop;
}
显然干事的是__mmc_claim_host,且其第二个参数为NULL。
23行,DECLARE_WAITQUEUE是一个宏,我们把它展开:
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } }
#define current (get_current())
DECLARE_WAITQUEUE(wait, current);
wait_queue_t wait = {
.private = get_current(),
.func = default_wake_function, \
.task_list = { NULL, NULL }
}
这里创建一个名为"wait"的等待队列并对其成员初始化,设置其private成员为当前进程。
27行,might_sleep()提醒使用代码的人,这个函数可能会睡眠。如果在原子型上下文(spinlock, irq-handler, …)调用,this macro will print a stack trace,当然开启该功能需要打开相应的config。这里不多说。
29行,add_wait_queue(&host->wq, &wait),把自己(“wait”)加入到等待队列中(host->wq)。host->wq在SDIO驱动(2)Host注册流程提到的mmc_alloc_host()函数中已经初始化。
30行,spin_lock_irqsave(&host->lock, flags),这个spinlock执行三个动作:保存当前中断标志(到flag),禁止CPU中断,获取lock;自然spin_unlock_irqrestore执行相反的操作。
31~39行之间的 while循环,首先设置进程状态为TASK_UNINTERRUPTIBLE不可中断,进而对信号不做响应,保证当前进程在等待时不会受到干扰。传进来的第二个参数为NULL,所以stop等于0。接着进行判断,如果我们获取了host的使用权则结束循环,否则恢复中断、释放spinlock并进行调度,当前进程进入睡眠状态等待唤醒。什么情况下唤醒?另一个进程release host的时候:
static void mmc_do_release_host(struct mmc_host *host)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
spin_unlock_irqrestore(&host->lock, flags);
} else {
host->claimed = 0;
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
}
}
很清楚了不是?
当前进程被唤醒, set_current_state(TASK_RUNNING)继续运行。
41~45行,表明host被当前进程占用了并增加计数。
46行移除等待队列,50行使能host。
sdio_claim_host(func);
ret = sdio_set_block_size(func, 0);
sdio_release_host(func);
这2个接口的配对使用,目的是为了独占式的使用host。