分类: LINUX
2012-03-30 14:19:17
如果让大家投票的话,usb_stor_control_thread()这个函数中的代码无疑是整个模块中最为精华的代码。我们只需要它中间 306行的for(;;)就知道,这是一个死循环,即使别的代码都执行完了,即使别的函数都退出了,这个函数也仍然像永不消逝的电波一般,经典常驻。显 然,只有死循环才能代表永恒,才能代表忠诚。这是每一个守护者的职责。
usb_stor_control_thread(),其代码如下:
299 static int usb_stor_control_thread(void *__us)
300 {
301 structus_data *us = (struct us_data *)__us;
302 structScsi_Host *host = us_to_host(us);
303
304 current->flags|= PF_NOFREEZE;
305
306 for(;;) {
307 US_DEBUGP("*** thread sleeping.\n");
308 if(down_interruptible(&us->sema))
309 break;
310
311 US_DEBUGP("*** thread awakened.\n");
312
313 /* lock the device pointers */
314 mutex_lock(&(us->dev_mutex));
315
316 /* if the device has disconnected, we are free to exit */
317 if (test_bit(US_FLIDX_DISCONNECTING,&us->flags)) {
318 US_DEBUGP("--exiting\n");
319 mutex_unlock(&us->dev_mutex);
320 break;
321 }
322
323 /* lock access to the state */
324 scsi_lock(host);
325
326 /* has the command timed out *already* ? */
327 if (test_bit(US_FLIDX_TIMED_OUT,&us->flags)) {
328 us->srb->result = DID_ABORT << 16;
329 goto SkipForAbort;
330 }
331
332 scsi_unlock(host);
333
334 /* reject the command if the direction indicator
335 *is UNKNOWN
336 */
337 if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
338 US_DEBUGP("UNKNOWN datadirection\n");
339 us->srb->result = DID_ERROR<< 16;
340 }
341
342 /* reject if target != 0 or if LUN is higher than
343 * the maximum known LUN
344 */
345 else if (us->srb->device->id&&
346 !(us->flags &US_FL_SCM_MULT_TARG)) {
347 US_DEBUGP("Bad target number(%d:%d)\n",
348 us->srb->device->id,us->srb->device->lun);
349 us->srb->result =DID_BAD_TARGET << 16;
350 }
351
352 else if (us->srb->device->lun >us->max_lun) {
353 US_DEBUGP("Bad LUN(%d:%d)\n",
354 us->srb->device->id,us->srb->device->lun);
355 us->srb->result =DID_BAD_TARGET << 16;
356 }
357
358 /* Handle those devices which need us to fake
359 * their inquiry data */
360 else if ((us->srb->cmnd[0] == INQUIRY)&&
361 (us->flags & US_FL_FIX_INQUIRY)) {
362 unsigned char data_ptr[36] = {
363 0x00, 0x80, 0x02, 0x02,
364 0x1F, 0x00, 0x00, 0x00};
365
366 US_DEBUGP("Faking INQUIRYcommand\n");
367 fill_inquiry_response(us, data_ptr, 36);
368 us->srb->result = SAM_STAT_GOOD;
369 }
370
371 /* we've got a command, let's do it! */
372 else {
373 US_DEBUG(usb_stor_show_command(us->srb));
374 us->proto_handler(us->srb, us);
375 }
376
377 /* lock access to the state */
378 scsi_lock(host);
379
380 /* did the command already complete because of a disconnect? */
381 if (!us->srb)
382 ; /* nothing to do */
383
384 /* indicate that the command is done */
385 else if (us->srb->result != DID_ABORT << 16) {
386 US_DEBUGP("scsi cmd done,result=0x%x\n",
387 us->srb->result);
388 us->srb->scsi_done(us->srb);
389 } else {
390 SkipForAbort:
391 US_DEBUGP("scsi commandaborted\n");
392 }
393
394 /* If an abort request was received we need to signal that
395 * the abort has finished. The proper test for this is
396 *the TIMED_OUT flag, not srb->result == DID_ABORT, because
397 *the timeout might have occurred after the command had
398 *already completed with a different result code. */
399 if (test_bit(US_FLIDX_TIMED_OUT,&us->flags)) {
400 complete(&(us->notify));
401
402 /* Allow USB transfers to resume */
403 clear_bit(US_FLIDX_ABORTING,&us->flags);
404 clear_bit(US_FLIDX_TIMED_OUT,&us->flags);
405 }
406
407 /* finished working on this command */
408 us->srb = NULL;
409 scsi_unlock(host);
410
411 /* unlock the device pointers */
412 mutex_unlock(&us->dev_mutex);
413 } /* for (;;) */
414
415 scsi_host_put(host);
416
417 /* notify the exit routine thatwe're actually exiting now
418 *
419 * complete()/wait_for_completion() issimilar to up()/down(),
420 * except that complete() is safe in the case where thestructure
421 *is getting deleted in a parallel mode of execution (i.e. just
422 * after the down() -- that's necessary for thethread-shutdown
423 *case.
424 *
425 * complete_and_exit() goes even further than this -- it is safein
426 * the case that the thread of the caller is going away (notjust
427 * the structure) -- this is necessary for the module-removecase.
428 * This is important in preemption kernels, which transfer theflow
429 * of execution immediately upon a complete().
430 */
431 complete_and_exit(&threads_gone,0);
432 }
302行,定义了一个Scsi_Host的指针host,令它指向us->host,也就是刚刚用scsi_host_alloc()申请的Scsi_Host结构体变量。
304行,这里为目前的进程设置一个flag,PF_NOFREEZE,在整个内核代码中,这个flag也只出现过几次。这个flag是与电源管理 相关的,2.6内核为了实现与Windows相似的一个功能——Hibernate,也就是“冬眠”,(Windows关机选项里面有“关机”,“重 启”,“注销",“Stand by”,以及“Hibernate”)。在内核编译菜单里面,Power managerment options中,有一个选项Software Suspend,也就是内核编译选项中的CONFIG_SOFTWARE_SUSPEND,选择了它使得机器可以被suspended(挂起)。显然咱们 不用在意它。但是这里之所以要设置这个flag,是因为suspend要求把内存里的内容写到磁盘上,而一个进程设置了这个flag就表明它在 suspend时不会被冻住,用“行话”来讲就是“they’renot refrigerated during a suspend.”freeze就是冷冻,冻住的意思,过去分词frozen是形容词,冻结的,冰冻的,其实就是让进程睡眠。所以总的来说,即使系统 “suspend”了,这个进程或者准确地说这个内核线程,也不应该进入睡眠。
306行,一个for语句死循环,尽管外面的世界很精彩,但是咱们不妨去查看for里面的世界。
308行,down_interruptible()函数,事实上307行的调式信息已经告诉我们,thread将进入睡眠了。 down_interruptible的参数是&us->sema,这是一把锁,而这里就是想获得这把锁,但是别忘了,这把锁一开始就被初 始化为0了,参考drivers/usb/storage/usb.c中第973行,storage_probe()函数:
973 init_MUTEX_LOCKED(&(us->sema));
也就是说它属于那种“指腹为婚”的情形,一到这个世界来就告诉别人自己已经是名花有主了。因此,这里只能进入睡眠,等待一个up()函数去释放锁。谁会调 用up()函数呢?暂时先不管它,我们先关注一下父进程,父进程在执行完wake_up_process(th)之后, usb_stor_acquire_resources()将结束,带着0返回了storage_probe()中去。