我们使用的是运行 3.2 版内核的定制板,但在测试 MMC 层的稳健性时遇到了一些问题。
首先,我们的MMC插槽没有卡检测引脚。有问题的问题包括以下内容:
- 加载模块 (omap_hsmmc)。卡在上电时被检测到,并被正确安装。
- 从 SD 卡中读取内容(即
cat foo.txt
) - 在读取文件时,取出卡。
- 在多次尝试失败后,系统挂起。
现在,我已将问题跟踪到以下代码部分drivers/mmc/card/queue.c
:
static int mmc_queue_thread(void *d)
{
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
current->flags |= PF_MEMALLOC;
down(&mq->thread_sem);
do {
struct request *req = NULL;
struct mmc_queue_req *tmp;
spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
req = blk_fetch_request(q);
mq->mqrq_cur->req = req;
spin_unlock_irq(q->queue_lock);
if (req || mq->mqrq_prev->req) {
set_current_state(TASK_RUNNING);
mq->issue_fn(mq, req);
} else {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
break;
}
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
}
/* 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;
} while (1);
up(&mq->thread_sem);
return 0;
}
调查这段代码,我发现它挂在mq->issue_fn(mq, req)
调用中。此函数准备并发出适当的命令来完成传递给它的请求,并且它知道当它无法与卡通信时发生的任何错误。该错误以一种尴尬(在我看来)的方式处理,并且不会“冒泡”到mmc_queue_thread
. 但是,我已经对照最新的内核版本(4.9)检查了我的版本代码,除了更好地分离每个错误情况(处理非常相似)之外,我没有发现在处理此类错误方面有任何区别)。
我怀疑这个问题是由于上层无法停止发出从 MMC 卡读取的新请求引起的。
我试过的:
- 重写了代码,以便将错误传递给我,以便我可以做
ret = mq->issue_fn(mq, req)
。 - 为了能够识别出具体的错误,我尝试以不同的方式取消线程:调用
kthread_stop
、调用mmc_queue_suspend
、__blk_end_request
等等。有了这些,我所能完成的最多就是保持线程处于“无害”状态,它仍然存在,但不消耗任何资源。但是,触发调用的用户空间程序没有返回,锁定在不可中断状态。
我的问题:
- 通过阻止上层发出新请求来解决这个问题的最佳方法是什么?还是应该“杀死”线程本身?
- 在我这样的情况下,是否应该假设因为没有卡片检测针,所以不应该取出卡片?
更新:我发现您可以告诉驱动程序使用轮询来检测卡插入/移除。可以通过在驱动程序初始化时将MMC_CAP_NEEDS_POLL
标志添加到 mmc来完成.caps
(在较新的内核上,您可以使用broken-cd
DT 上的属性)。但是,修改后问题仍然存在。