1

我们使用的是运行 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(在较新的内核上,您可以使用broken-cdDT 上的属性)。但是,修改后问题仍然存在。

4

1 回答 1

1

想出了解决办法!

正如我所怀疑的那样,问题是即使在卡被移除后仍会继续发出阻止请求。该错误已在德克萨斯州内核存储库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>

提交中的关键是 areturn BLKPREP_KILL被添加到 中mmc_prep_request(),除了一些小的调整,为错误处理添加了一些“粒度”,为卡被移除的情况添加了一个特定的代码。

于 2017-02-01T12:19:26.143 回答