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

Stop MMC queue from fetching new requests when communication with card times out

我们使用的是自定义开发板 运行 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) 调用中。此功能准备并发出适当的命令来满足传递给它的请求,并且它知道在它无法与卡通信时发生的任何错误。该错误的处理方式很尴尬(在我看来),并且没有 'bubble up' 到 mmc_queue_thread。但是,我已经将我的版本代码与最新内核版本 (4.9) 的代码进行了比较,我没有发现在处理此类错误方面有任何区别,除了更好地分离每个错误案例(处理方式非常相似) ).

我怀疑问题是由于上层无法停止从MMC卡读取新请求造成的。


我尝试过的:


我的问题:

  1. 最好的解决办法是阻止上层提出新的请求吗?或者线程本身应该是 'killed off'?
  2. 像我这样的情况,是不是应该认为因为没有卡检测针,卡不应该被取出?

更新: 我发现你可以告诉驱动程序使用轮询来检测卡 insertion/removal。这可以通过在驱动程序初始化时将 MMC_CAP_NEEDS_POLL 标志添加到 mmc 的 .caps 来完成(在较新的内核上,您可以在 DT 上使用 broken-cd 属性)。但是修改后还是出现这个问题

找出解决方案!

正如我所怀疑的那样,问题是即使在卡被移除后,阻止请求仍在发出。该错误已在提交 a8ad82cc1b22d04916d9cdb1dc75052e80ac803c from Texas' kernel repository:

中修复
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>

提交的关键是 return BLKPREP_KILL 被添加到 mmc_prep_request(),除了一些小的调整,添加了一些 'granularity' 到错误处理,添加了一个特定的代码卡被移除的情况。