Linux那些事儿之我是UHCI(大结局)"脱"就一个字

来源:互联网 发布:python 每秒执行一次 编辑:程序博客网 时间:2024/04/29 03:15

李小璐脱了,周迅脱了,汤唯脱了.下一个脱的是谁?

答案不是林志玲,不是徐静蕾,而是QH.我们知道整个故事里我们一直围绕着QH的队列在说来说去,我们不停的进行着队列操作,我们有时候把QH link起来成一个个的队列,而有时候又把QH从队列里给unlink,我所用的盗版的金山词霸2005告诉我,unlink翻译成中文就是解开,拆开,松开.Okay,简洁一点说,一个字,!脱就脱,东风吹,战鼓擂,这个世界谁怕谁?

但问题是明星们脱了就走红,她们不仅不要担心嫁不出去,相反她们身价暴涨,相反她们成为无数男人性幻想对象.那么QH脱了之后会怎么样?是不是就废了?还有人愿意要它吗?

你放心,它们也不会没人要.还记得skel_unlink_qh?skelqh[]数组里边11个元素,另外那10个咱们都知道怎么回事了,但是其中这第一个元素,或者说这0号元素,其实咱们一直就不太明白.现在咱们就来仔细解读一下.

事实上,uhci_unlink_qh这个函数有这么一句话,list_move_tail(&qh->node,&uhci->skel_unlink_qh->node),换言之,凡是调用过uhci_unlink_qhqh,最终都被加入到了由skel_unlink_qh领衔的孤魂野鬼队列.但问题是加入这个队列之后呢?是不是就算隐退江湖了?其实不然,生活哪有那么简单啊?不是想退出江湖就能退出江湖的.咱们回过头来看这个函数,uhci_scan_schedule,

   1705 /*

   1706  * Process events in the schedule, but only in one thread at a time

   1707  */

   1708 static void uhci_scan_schedule(struct uhci_hcd *uhci)

   1709 {

   1710         int i;

   1711         struct uhci_qh *qh;

   1712

   1713         /* Don't allow re-entrant calls */

   1714         if (uhci->scan_in_progress) {

   1715                 uhci->need_rescan = 1;

   1716                 return;

   1717         }

   1718         uhci->scan_in_progress = 1;

   1719 rescan:

   1720         uhci->need_rescan = 0;

   1721         uhci->fsbr_is_wanted = 0;

   1722

   1723         uhci_clear_next_interrupt(uhci);

   1724         uhci_get_current_frame_number(uhci);

   1725         uhci->cur_iso_frame = uhci->frame_number;

   1726

   1727         /* Go through all the QH queues and process the URBs in each one */

   1728         for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {

   1729                 uhci->next_qh = list_entry(uhci->skelqh[i]->node.next,

   1730                                 struct uhci_qh, node);

   1731                 while ((qh = uhci->next_qh) != uhci->skelqh[i]) {

   1732                         uhci->next_qh = list_entry(qh->node.next,

   1733                                         struct uhci_qh, node);

   1734

   1735                         if (uhci_advance_check(uhci, qh)) {

   1736                                 uhci_scan_qh(uhci, qh);

   1737                                 if (qh->state == QH_STATE_ACTIVE) {

   1738                                         uhci_urbp_wants_fsbr(uhci,

   1739         list_entry(qh->queue.next, struct urb_priv, node));

   1740                                 }

   1741                         }

   1742                 }

   1743         }

   1744

   1745         uhci->last_iso_frame = uhci->cur_iso_frame;

   1746         if (uhci->need_rescan)

   1747                 goto rescan;

   1748         uhci->scan_in_progress = 0;

   1749

   1750         if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&

   1751                         !uhci->fsbr_expiring) {

   1752                 uhci->fsbr_expiring = 1;

   1753                 mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);

   1754         }

   1755

   1756         if (list_empty(&uhci->skel_unlink_qh->node))

   1757                 uhci_clear_next_interrupt(uhci);

   1758         else

   1759                 uhci_set_next_interrupt(uhci);

   1760 }

咱们看1728行这个for循环,skelqh[]数组从0开始循环,直到9,19咱就不说了,0,咱们顺着0往下看,针对skel_unlink_qh队伍里的每一个qh进行循环,每一个qh执行一次uhci_advance_check(),skel_unlink_qh这个队伍里的qh有一部分是上一次刚刚在uhci_advance_check中设置了wait_expired1,另一部分可能没有设置过,因为调用uhic_unlink_qh()的并非只有uhci_advance_check(),还有别的地方,而别的地方调用它的话就和超时不超时没有关系了.

于是我们现在分两种情况来看待这个uhci_advance_check.第一种,qh是因为超时被拉进skel_unlink_qh,那么1673if条件是满足的,这种情况下uhci_advance_check就直接返回了,但是返回的肯定是1.返回了之后来到uhci_scan_schedule,1736,uhci_scan_qh就会被执行,进入到uhci_scan_qh,1602,由于qh中还有urb,1617行的uhci_activate_qh就会被执行,因此qh将重新激活,qh->state会被设置为QH_STATE_ACTIVE,它会再次被拉入它自己的归属.于是它又幸运的获得了重生.

而对于第二种情况,也是通过uhci_unlink_qh()给拉入skel_unlink_qh.但是人家起码没超时,所以这次咱们再看uhci_advance_check的话,1673行这个if条件就不一定满足了.然后如果真没超时,那么1697行会被执行,1697这个if条件是否满足得看1654行这个if是否满足了,如果1654行满足,换言之,qh->state不是QH_STATE_ACTIVE,则设置urbp为空,而我们知道uhci_unlink_qh会把qh->state设置为QH_STATE_UNLINKING,所以,1654行肯定满足,所以urbp设置为了NULL,因此1697行这个if不会满足.因此,对于unlinkqh,咱们这次uhci_advance_check这次除了返回1其它什么也不做,但是回到了uhci_scan_schedule之后,uhci_scan_qh会执行,1622.

   1529 /*

   1530  * Scan the URBs in a QH's queue

   1531  */

   1532 #define QH_FINISHED_UNLINKING(qh)                       /

   1533                 (qh->state == QH_STATE_UNLINKING &&     /

   1534                 uhci->frame_number + uhci->is_stopped != qh->unlink_frame)

首先qh->unlink_frame是当初在uhci_unlink_qh()中设置的,设置的就是当时的Frame.uhci->frame_number是当前的frame.但对于眼前这个宏的含义,我曾经一度困惑过.我猜测这个宏判断的就是一个qh是否已经彻底失去利用价值,但我并不清楚为什么这个宏被这样定义.后来,Alan Stern大侠语重心长的教育我说:

When a QH is unlinked, the controller is allowed to continue using it until the end of the frame. So the unlink isn't finished until the frame is over and a new frame has begun. qh->unlink_frame is the frame number when the QH was unlinked. uhci->frame_number is the current frame number. If the two are unequal then the unlink is finished.

没错,当一个QH在某个frameunlink了之后,在这个frame结束之后主机控制器就不会再使用它了.也就是说,到下一个frame开始,这个QH就算是真正的彻底的完成了它的”.

这里尤其需要注意的是uhci->is_stopped,顾名思义,uhci正常工作的时候这个值应该为0,而只有uhci停了下来的时候,这个值才会是非0.但我们知道,如果主机控制器自己都停止了下来,那么显然这个qh就算是彻底脱了,因为主机控制器不可能再使用它了,或者说主机控制器不可能再访问它了,而停下来了就意味着is_stopped不为0,显然,只要is_stopped不为0,uhci->frame_number+uhci->is_stopped是不可能等于qh->unlink_frame.(uhci->frame_number>=qh->unlink_frame恒成立,is_stopped事实上永远大于等于0)

所以,1622行这个宏这么一判断,发现qh确实已经没有利用价值了,就调用uhci_make_qh_idle从而把qh->state设置为QH_STATE_IDLE,并且把本QH拉入uhci->idle_qh_list,一旦加入这个list,这个QH将从此永不见天日,没有人会再去理睬它了.

不过,最后,关于uhci_scan_qh,有三点需要强调一下.

第一点,1569行调用了uhci_giveback_urb(),为何在1594行也调用了uhci_giveback_urb.但事实上你会发现任何一个urb都不可能在这两处先后被调用,要么在前者被调用,要么在后者被调用.道理很简单,咱们在N年前就贴出过uhci_giveback_urb的代码,一旦调用了uhci_giveback_urb(),那么其urbp就会脱离qh的队列.这是uhci_giveback_urb()1507行那个list_del_init干的,甚至urbp的内存也会被释放掉,这是uhci_giveback_urb()1514行那个uhci_free_urb_priv()函数干的.

所以,事实上,对于大多数正常工作正常结束的urb,uhci_scan_qh,1569行这个uhci_giveback_urb会被调用,而一旦调用了,这个urbp就不复存在了,因此之后的代码就跟它半毛钱关系也没有了.

那么另一方面,1551行和1571行这两个break语句的存在使得while循环有可能提前结束,这就意味着,while循环结束的时候,qh->queue里面的urbp并不一定全部被遍历到了,因此,也就是说有些urb可能并没有执行1569行这个uhci_giveback_urb(),因此它们就有可能在1594行被传递给uhci_giveback_urb.

第二点,1595行这个goto restart是什么意思?乍一看,哥们儿我愣是以为这个goto restart会导致这段代码成为死循环,可后来我算是琢磨出来了,list_for_each_entry不是想遍历qh->queue这个urbp构成的队列么,可是每次如果它走到1594行这个uhci_giveback_urb的话,urbp会被删掉,于是队列就改变了,好家伙,你在调用list_for_each_entry遍历队列的时候改变队列那还能不出事?所以咱也就甭犹豫了,重新调用list_for_each_entry,重新遍历不就成了么?

第三点,虽然uhci_scan_qh这个函数看上去挺复杂,但是正如1574行这个注释所说的那样,事实上对于大多数情况(normal case)来说,这个函数执行到1579行就会返回.只有两种情况下才会执行1579之后的代码,第一个就是1576行这个QH_FINISHED_UNLINKING条件满足,即这个qh是刚刚被unlink刚刚完成,这种情况下要继续往下走,第二个就是虽然不是完成了脱的,但是is_stopped是不为0.

但是虽然说这两种情况是小概率事件,但毕竟咱们这节讨论的就是QH_FINISHED_UNLINKING,所以这种情况究竟怎么处理咱们还是要关注的.而这其中除了1623行这个uhci_make_qh_idle是最后一步要做的事情之外,1590,uhci_cleanup_queue咱们也没有仔细看过.既然咱们整个故事已经进入到了cleanup的阶段,那么就让咱们以这个cleanup函数作为结束吧.它来自drivers/usb/host/uhci-q.c,咱们之前其实也贴过,只是没有讲过罢了.

    312 /*

    313  * When a queue is stopped and a dequeued URB is given back, adjust

    314  * the previous TD link (if the URB isn't first on the queue) or

    315  * save its toggle value (if it is first and is currently executing).

    316  *

    317  * Returns 0 if the URB should not yet be given back, 1 otherwise.

    318  */

    319 static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,

    320                 struct urb *urb)

    321 {

    322         struct urb_priv *urbp = urb->hcpriv;

    323         struct uhci_td *td;

    324         int ret = 1;

    325

    326         /* Isochronous pipes don't use toggles and their TD link pointers

    327          * get adjusted during uhci_urb_dequeue().  But since their queues

    328          * cannot truly be stopped, we have to watch out for dequeues

    329          * occurring after the nominal unlink frame. */

    330         if (qh->type == USB_ENDPOINT_XFER_ISOC) {

    331                 ret = (uhci->frame_number + uhci->is_stopped !=

    332                                 qh->unlink_frame);

    333                 goto done;

    334         }

    335

    336         /* If the URB isn't first on its queue, adjust the link pointer

    337          * of the last TD in the previous URB.  The toggle doesn't need

    338          * to be saved since this URB can't be executing yet. */

    339         if (qh->queue.next != &urbp->node) {

    340                 struct urb_priv *purbp;

    341                 struct uhci_td *ptd;

    342

    343                 purbp = list_entry(urbp->node.prev, struct urb_priv, node);

    344                 WARN_ON(list_empty(&purbp->td_list));

    345                 ptd = list_entry(purbp->td_list.prev, struct uhci_td,

    346                                 list);

    347                 td = list_entry(urbp->td_list.prev, struct uhci_td,

    348                                 list);

    349                 ptd->link = td->link;

    350                 goto done;

    351         }

    352

    353         /* If the QH element pointer is UHCI_PTR_TERM then then currently

    354          * executing URB has already been unlinked, so this one isn't it. */

    355         if (qh_element(qh) == UHCI_PTR_TERM)

    356                 goto done;

    357         qh->element = UHCI_PTR_TERM;

    358

    359         /* Control pipes don't have to worry about toggles */

    360         if (qh->type == USB_ENDPOINT_XFER_CONTROL)

    361                 goto done;

    362

    363         /* Save the next toggle value */

    364         WARN_ON(list_empty(&urbp->td_list));

    365         td = list_entry(urbp->td_list.next, struct uhci_td, list);

    366         qh->needs_fixup = 1;

    367         qh->initial_toggle = uhci_toggle(td_token(td));

    368

    369 done:

    370         return ret;

    371 }

首先注释里说的也很清楚.

330,对于ISO类型,它并不使用toggle bits,所以这里就是判断是否彻底,是就返回1.

339,如果当前讨论的这个urb不是qh->queue队列里的第一个urb,那么就往if里面的语句,purbp将是urbp的前一个节点,即前一个urbp,ptd则是purbptd队列中最后一个td,td又是urbptd队列中最后一个td.ptdlink指向tdlink,即让前一个urbp的最后一个td指向原来又本urbp的最后一个td所指向的位置.有了接班人之后,当前这个urb或者说这个urbp就可以淡出历史舞台了.

357,qh->element等于UHCI_PTR_TERM,等于宣布本qh正式退休.

365,366,367行的目的也很明确,保存好下一个tdtoggle.以待时机进行fix.至于如何fix,咱们在讲uhci_giveback_urbuhci_scan_qh中都已经看到了,会通过判断needs_fixup来执行相应的代码.此处不再赘述.

关于”,就讲到这里吧.

而关于UHCI,也就讲到这里吧.也算是纪念自己北漂生活一周年.