《Linux那些事儿之我是USB》我是U盘(23)彼岸花的传说(二)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fudan_abc/article/details/6904843

如果让大家投票的话,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()中去。
展开阅读全文

没有更多推荐了,返回首页