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

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

答案不是林志玲,不是徐静蕾,而是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来执行相应的代码.此处不再赘述.

关于”脱”,就讲到这里吧.

而关于UHCI,也就讲到这里吧.也算是纪念自己北漂生活一周年.一直想去旅行,去很美很美的地方,但往往真正踏足想去的地方,

Linux那些事儿之我是UHCI(大结局)&quot;脱&quot;就一个字 – fuda

相关文章:

你感兴趣的文章:

标签云: