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_qh的qh,最终都被加入到了由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,1到9咱就不说了,而0,咱们顺着0往下看,针对skel_unlink_qh队伍里的每一个qh进行循环,每一个qh执行一次uhci_advance_check(),而skel_unlink_qh这个队伍里的qh有一部分是上一次刚刚在uhci_advance_check中设置了wait_expired为1的,另一部分可能没有设置过,因为调用uhic_unlink_qh()的并非只有uhci_advance_check(),还有别的地方,而别的地方调用它的话就和超时不超时没有关系了.
于是我们现在分两种情况来看待这个uhci_advance_check.第一种,qh是因为超时被拉进skel_unlink_qh的,那么1673行if条件是满足的,这种情况下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不会满足.因此,对于unlink的qh,咱们这次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在某个frame被unlink了之后,在这个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则是purbp的td队列中最后一个td,而td又是urbp的td队列中最后一个td.让ptd的link指向td的link,即让前一个urbp的最后一个td指向原来又本urbp的最后一个td所指向的位置.有了接班人之后,当前这个urb或者说这个urbp就可以淡出历史舞台了.
357行,让qh->element等于UHCI_PTR_TERM,等于宣布本qh正式退休.
365,366,367行的目的也很明确,保存好下一个td的toggle位.以待时机进行fix.至于如何fix,咱们在讲uhci_giveback_urb和uhci_scan_qh中都已经看到了,会通过判断needs_fixup来执行相应的代码.此处不再赘述.
关于”脱”,就讲到这里吧.
- Linux那些事儿之我是UHCI(大结局)"脱"就一个字
- Linux那些事儿之我是UHCI-引子
- Linux那些事儿之我是Hub(大结局)挂起自动化
- Linux那些事儿之我是Hub(大结局)挂起自动化
- Linux那些事儿之我是UHCI(3)-物以类聚
- Linux那些事儿之我是UHCI(5)传说中的DMA
- Linux那些事儿之我是UHCI(16)寂寞在唱歌
- Linux那些事儿之我是UHCI(24)等时传输
- Linux那些事儿之我是UHCI(29)FSBR
- Linux那些事儿之我是UHCI(5)传说中…
- Linux那些事儿之我是UHCI(5)传说中…
- Linux那些事儿之我是UHCI(10)一个函数引发的故事(一)
- Linux那些事儿之我是UHCI(11)一个函数引发的故事(二)
- Linux那些事儿之我是UHCI(12)一个函数引发的故事(三)
- Linux那些事儿之我是UHCI(13)一个函数引发的故事(四)
- Linux那些事儿之我是UHCI(14)一个函数引发的故事(五)
- Linux那些事儿之我是UHCI(6)来来,我是一条总线,线线线线线线
- Linux那些事儿之我是UHCI(1)开户和销户
- 翻译(一)SharePoint 2007管理入门:Windows SharePoint Service 3.0 and Microsoft Office SharePoint Server 2007
- 矩阵乘法
- KMP算法
- webBrowser控制新窗口
- Linux那些事儿之我是UHCI(29)FSBR
- Linux那些事儿之我是UHCI(大结局)"脱"就一个字
- Proxool Provider unable to load JAXP configurator file: proxool.xml
- ACE研读笔记之五-循环式日志服务器
- 配置连接池
- php编程常见问题
- log4j 配置
- JMS(Jboss Messaging)的一点使用心得(五)Spring扩展应用-可自动重连的JmsMessageListenerContainer
- SMTP服务器的构建 ( Postfix ) > (最近更新日:2006/09/28)
- POP / IMAP 服务器的构建( Dovecot )