当与卡的通信超时时,停止MMC队列获取新请求



我们使用的是运行3.2版本内核的自定义板,但在测试MMC层的健壮性时遇到了一些问题。

首先,我们的MMC插槽没有卡检测引脚。问题包括以下内容:

  1. 加载模块(omap_hsmmc)。通电时检测到卡,并且被适当地安装
  2. 从SD卡中读取内容(即cat foo.txt)
  3. 在读取文件时,取出卡
  4. 在多次尝试失败后,系统将挂起

现在,我已经将问题跟踪到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等。有了这些,我所能完成的就是将线程保持在"无害"状态,它仍然存在,但不消耗任何资源。但是,触发调用的用户空间程序不会返回,锁定在不间断状态

我的问题:

  1. 解决这个问题的最佳方法是阻止上层发出新请求吗?还是应该"杀死"线程本身
  2. 在像我这样的情况下,是否应该假设因为没有卡检测引脚,所以不应该移除卡

更新:我发现你可以告诉驱动程序使用轮询来检测卡的插入/移除。可以在驱动程序初始化时将MMC_CAP_NEEDS_POLL标志添加到mmc的.caps中(在较新的内核上,可以在DT上使用broken-cd属性)。然而,在这种修改之后,问题仍然存在。

找到了解决方案!

正如我所怀疑的那样,问题是即使在卡被移除后,阻止请求仍在不断发出。该错误已在德克萨斯州内核存储库的提交a8ad82cc1b22d04916d9cdb1dc75052e80ac803c中修复

commit a8ad82cc1b22d04916d9cdb1dc75052e80ac803c
Author: Sujit Reddy Thumma <sthumma@codeaurora.org>
Date:   Thu Dec 8 14:05:50 2011 +0530
mmc: card: Kill block requests if card is removed
Kill block requests when the host realizes that the card is
removed from the slot and is sure that subsequent requests
are bound to fail. Do this silently so that the block
layer doesn't output unnecessary error messages.
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Chris Ball <cjb@laptop.org>

提交的关键是在mmc_prep_request()中添加了一个return BLKPREP_KILL,此外还有一些小的调整,为错误处理添加了一些"粒度",为移除卡的情况添加了特定的代码。

相关内容

最新更新