`
dato0123
  • 浏览: 946587 次
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是U盘(53)有多少爱可以胡来?(二)

 
阅读更多

device_reset()完了之后我们来看bus_reset().同样来自drivers/usb/storage/scsiglue.c.

265 /* This resets the device's USB port. */
266 /* It refuses to work if there's more than one interface in
267 * the device, so that other users are not affected. */
268 /* This is always called with scsi_lock(srb->host) held */
269 static int bus_reset(struct scsi_cmnd *srb)
270 {
271 struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
272 int result, rc;
273
274 US_DEBUGP("%s called/n", __FUNCTION__);
275
276 scsi_unlock(srb->device->host);
277
278 /* The USB subsystem doesn't handle synchronisation between
279 * a device's several drivers. Therefore we reset only devices
280 * with just one interface, which we of course own. */
281
282 down(&(us->dev_semaphore));
283 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
284 result = -EIO;
285 US_DEBUGP("No reset during disconnect/n");
286 } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
287 result = -EBUSY;
288 US_DEBUGP("Refusing to reset a multi-interface device/n");
289 } else {
290 rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
291 if (rc < 0) {
292 US_DEBUGP("unable to lock device for reset: %d/n", rc);
293 result = rc;
294 } else {
295 result = usb_reset_device(us->pusb_dev);
296 if (rc)
297 usb_unlock_device(us->pusb_dev);
298 US_DEBUGP("usb_reset_device returns %d/n", result);
299 }
300 }
301 up(&(us->dev_semaphore));
302
303 /* lock the host for the return */
304 scsi_lock(srb->device->host);
305 return result < 0 ? FAILED : SUCCESS;
306 }

看完了device_reset再来看bus_reset应该就不难了.这个函数本身写的也特别清楚,注释很详细加上又有一些调试信息,基本上,中关村那些抱着小孩卖毛片的妇女同志们应该都能看懂这个函数了.咱们再简单介绍一下,device_reset不同的地方在于,device_reset实际上只是设备本身的复位,bus_reset的意义就更加广泛了,它涉及到从usb core这一层面来初始化这个设备,千万不要混淆了,bus reset不是说usb busreset,而是scsi bus,scsi总线.哪来的scsi总线?我们模拟的,假的.难道你忘记了我们模拟了一个scsi host,一个scsi device,实际上也就是模拟了一条scsi总线.而这个冬冬的reset就意味着整个driver都得重新初始化,即从usb core那边开始,确切的说是usb hub那边,先给咱们在usb总线上来重新分配一个地址,重新建立起之前的配置,重新选择altsetting,也就是说对于咱们这个设备,相当于重新经历了一次usb的总线枚举.而之后,咱们的storage_probe()会重新被调用,整个模拟的scsi总线将从头再来,所以说bus_reset是一场很大规模的运动,而不像device reset那样属于小打小闹式的.

283,还是老套路,检查是不是disconnecting,要是的话就甭reset,省省事吧,别瞎折腾了.

286,还记得咱们说过一个interface对应一个driver,一个device可能有多个interface,也因此可能对应多个driver,那么咱们这里对一个device reset就只能针对那种一个device只有一个interface的情况,因为要不然您就影响别人了,就好比,假如你们家是在一个森林里边,前不挨村后不着店的房子,或者说你们家的周边地形和那个史上最牛的重庆钉子户差不多,那您如果想把自己建好的房子给拆了,那可能没人管,但假如您住的是楼房,您的卧室跟邻居家的卧室一墙之隔,或者浴室跟邻居家的浴室一墙之隔,那您要是敢把您那堵墙拆了,毋庸置疑,邻居非跟您急不可.

然后289行到300,总共三个来自usb core那边的关键函数,usb_lock_device_for_reset/usb_reset_device/usb_unlock_device.usb core都为您准备好了,您要从总线上来reset设备,首先调用的是usb_lock_device_for_reset,这是usb核心层的函数,来自drivers/usb/core/usb.c,成功执行就返回1或者0,失败了就返回一个负的错误代码.295行的usb_reset_device(),同样来自usb核心层,drivers/usb/core/hub.c,usb核心层提供给咱们的用来重新初始化一个设备的函数.成功返回0,否则就返回负的错误代码.出错了就得调用usb_unlock_device来释放锁.

最后305,result0才是返回成功,否则返回FAILED.

能看懂296,297行吗?我们说了,usb_lock_device_for_reset可以返回负数,可以返回1,可以返回0.返回负数的情况就是291293行所做的事情,而对于usb_lock_device_for_reset来说,它返回1表示我们在执行了usb_reset_device之后要调用usb_unlock_device来释放锁,而返回0表示我们在执行了usb_reset_device之后不需要调用usb_unlock_device来释放锁.如果你对这些很好奇,那么你可以看一下usb core的代码.特别是看一下关于两个宏的代码,USB_INTERFACE_BINDINGUSB_INTERFACE_BOUND.

于是,我们就这样从这个bus_reset()函数打马而过了.

最后我们来看command_abort().很显然这是一个错误处理函数, 她的职责很明确, 试图去中止当前的命令.同样来自drivers/usb/storage/scsiglue.c:

202 /* Command timeout and abort */
203 /* This is always called with scsi_lock(srb->host) held */
204 static int command_abort(struct scsi_cmnd *srb )
205 {
206 struct Scsi_Host *host = srb->device->host;
207 struct us_data *us = (struct us_data *) host->hostdata[0];
208
209 US_DEBUGP("%s called/n", __FUNCTION__);
210
211 /* Is this command still active? */
212 if (us->srb != srb) {
213 US_DEBUGP ("-- nothing to abort/n");
214 return FAILED;
215 }
216
217 /* Set the TIMED_OUT bit. Also set the ABORTING bit, but only if
218 * a device reset isn't already in progress (to avoid interfering
219 * with the reset). To prevent races with auto-reset, we must
220 * stop any ongoing USB transfers while still holding the host
221 * lock. */
222 set_bit(US_FLIDX_TIMED_OUT, &us->flags);
223 if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
224 set_bit(US_FLIDX_ABORTING, &us->flags);
225 usb_stor_stop_transport(us);
226 }
227 scsi_unlock(host);
228
229 /* Wait for the aborted command to finish */
230 wait_for_completion(&us->notify);
231
232 /* Reacquire the lock and allow USB transfers to resume */
233 scsi_lock(host);
234 clear_bit(US_FLIDX_ABORTING, &us->flags);
235 clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
236 return SUCCESS;
237 }

既然是阻止某个命令,那么传递进来的参数当然是struct scsi_cmnd的指针了.前两行的赋值无须多说.

212,us->srb表示的是当前的srb,srb是这个函数传进来的参数,command_abort()希望的是中止当前的命令,而假如传进来的参数根本就不是当前的命令,那么肯定有问题.啥也不说了,直接返回FAILED.

然后,abort的时刻,设置US_FLIDX_TIMED_OUTflag,然后如果设备没有reset,那么继续再设置另一个flag,US_FLIDX_ABORTING,然后调用usb_stor_stop_transport(),这个函数的目的很简单,阻止任何进行中的传输,这个函数定义于drivers/usb/storage/transport.c:

730 /* Stop the current URB transfer */
731 void usb_stor_stop_transport(struct us_data *us)
732 {
733 US_DEBUGP("%s called/n", __FUNCTION__);
734
735 /* If the state machine is blocked waiting for an URB,
736 * let's wake it up. The test_and_clear_bit() call
737 * guarantees that if a URB has just been submitted,
738 * it won't be cancelled more than once. */
739 if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
740 US_DEBUGP("-- cancelling URB/n");
741 usb_unlink_urb(us->current_urb);
742 }
743
744 /* If we are waiting for a scatter-gather operation, cancel it. */
745 if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
746 US_DEBUGP("-- cancelling sg request/n");
747 usb_sg_cancel(&us->current_sg);
748 }
749 }
这里有两个flag,US_FLIDX_URB_ACTIVEUS_FLIDX_SG_ACTIVE,前一个flag咱们此前在讲storage_probe()的时候遇见过,当时咱们为了获得max lun,曾经提交过urb,而在usb_stor_msg_common()函数中,urb被成功提交了之后,调用set_bit()宏为us->flags设置了这一位,于是这里就可以先判断,如果确实设置了,那么此时就可以调用usb_unlink_urb来从urb队列中取下来,因为没有必要再连接在上面了.

US_FLIDX_SG_ACTIVE这个flag咱们也不陌生,bulk传输里会用到.设置这个flag的是bulk传输中的函数usb_stor_bulk_transfer_sglist(),她会调用set_bit函数来设置这个flag. 于是这里也判断,如果确实设置了,那么此时就可以调用usb_sg_cancel来把sg取消.

看得出,usb_stor_stop_transport()的决心很坚定,目标很简单,就是要让你的传输进行不下去,urb就取消urb,sg就取消sg,大有人挡杀人佛当杀佛的魄力和勇气.

这时,230,咱们看到了等待函数,wait_for_completion(&us->notify),command_abort()函数进入等待.谁来唤醒它呢?usb_stor_control_thread()中的complete(&(us->notify))唤醒她.

一种情况,您这里睡眠中,usb设备断开了,于是usb_stor_control_thread要退出来,于是它会调用complete_and_exit()来唤醒command_abort()并且退出usb_stor_control_thread(),这样一来的话那么世界从此就清静了.

第二种情况,一个scsi命令还没有开始执行,或者说即将要开始执行的时候,我们调用了command_abort(),那么对应于usb_stor_control_thread()中的322325那几行的if语句,发现US_FLIDX_TIMED_OUT flag被设置了,于是,命令也甭执行了,直接goto SkipForAbort,然后调用complete(&(us->notify))唤醒咱们这里进入睡眠的进程.同时,对于usb_stor_control_thread()这边来说,us->srb设置为NULL就一切ok,可以开始下一轮循环了.

第三种情况, 一个scsi命令执行完了之后,我们才执行的command_abort,那么这种情况下,由于我们设置了US_FLIDX_TIMED_OUT标志,对应于usb_stor_control_thread()那边的396,397行的if语句,也没什么特别的,同样的,执行complete(&(us->notify))唤醒咱们这个进入睡眠的进程吧.并且将us->srb置为NULL.

最后,234,235,清除这两个US_FLIDX_ABORTING US_FLIDX_TIMED_OUT这两个flag,接下来的传输还是外甥打灯笼-照旧.command_abort()函数本身当然返回SUCCESS.阻止了别人,她就成功了.所以它返回SUCCESS.

至此我们就算讲完了这三个负责错误处理的函数了.那么关于这两个flag的故事呢.让我们来看看.实际上你搜索一下整个内核目录,只有command_abort()函数中set了这两个flag,而如果command_abort()执行成功了,会在返回之前,把两个flag给清除掉.而需要测试US_FLIDX_TIMED_OUT这个flag的地方,仅仅只有usb_stor_control_thread()中和usb_stor_invoke_transport().关于前者正是我们刚才所讲的那样,除了唤醒command_abort()以外,就是设置us->srbNULL,但总的来说,基本上对scsi那边没有什么影响.而在usb_stor_invoke_transport(),如果我们遇到了US_FLIDX_TIMED_OUT被设置,那么情况会有所不同,因为这时候我们是在与scsi层打交道,我们是因为要执行一个scsi命令才进入到usb_stor_invoke_transport(),所以在这种情况下如果遇到了abort,那么我们至少得给scsi一个交待,正如我们在usb_stor_invoke_transport()中第724行那一小段看到的那样,我们回复给scsi层的是srb->result,我们把它设成了DID_ABORT<<16.同时,对于Bulk-only的设备,我们还需要把我们的设备进行reset.这是bulk-only的传输协议里边规定的,,在一个abort之后,必须要进行一次reset,要不设备下次没法工作.(参见Bulk-only transport 5.3.3.1 Phase Error/5.3.4 Reset Recovery)

再来看另一个flag,US_FLIDX_ABORTING.实际上没有人专门去测试这个flag有没有设置,真正被测试的是另一个flag,ABORTING_OR_DISCONNECTING,关于这个flag的定义我们早年曾经列出来过,实际上意思很明显,或者是设置了Abortingflag,或者就是设置了Disconnectingflag.很多情况下我们实际上要判断的就是这两者之中任何一个是否被设置了,因为很显然,很多情况下,如果这两个中的任何一个被设置了,那么我们的某些事情就没有必要继续了,因为当你明明知道你们之间没有未来,你有何必浪费双方的感情呢?(当然,一夜情不在我们讨论的范畴之内.)关于ABORTING_OR_DISCONNECTING,一共有两个函数中需要判断它,一个是usb_stor_msg_common(),一个是usb_stor_bulk_transfer_sglist().其实,仔细回顾一下这两个函数,你就不难发现,usb_stor_msg_common(),其实我们就是在提交一个urb之前先检查这个flag有没有设置,提交了之后再次检查一下,实际上就相当于是给你两次机会,因为提交了之后就相当于表白了之后,但是双方可能还没有发生什么实质性的故事,在这一刻如果你意识到这是一段没有结果的恋情,你还可以选择放弃.但是过了这第二次判断,那么结果就将是生米煮成熟饭了.而在usb_stor_bulk_transfer_sglist(),其实道理是一样的,就是把urb的概念换成sg,仅此而已.

Ok,到现在我们可以来看最后一个重量级的flag.那就是US_FLIDX_DISCONNECTING.而将带我们走入我们整个故事的最后一个重量级函数, storage_disconnect().而这个函数也将宣告我们整个故事的结束,毕竟天下无不散的宴席,从我开始写这个故事的时候,我就知道一切都已注定,注定了开始,注定了结束.

分享到:
评论

相关推荐

    Linux那些事儿之我是U盘

    ### Linux中的USB子系统详解——《Linux那些事儿之我是U盘》 #### 一、引言 在现代计算环境中,USB(Universal Serial Bus)已成为连接各种外围设备的标准接口之一。无论是移动存储设备如U盘,还是键盘、鼠标等输入...

    Linux那些事儿

    Linux那些事儿之我是U盘 Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是...

    Linux那些事儿之我是USB(第2版)

    通过以上对《Linux那些事儿之我是USB》第二版的深入解析,我们可以看出这本书不仅是一本技术指南,更是一种学习态度的倡导者。无论是对于初学者还是资深开发者,都能从中获得宝贵的知识和启示。

    Linux那些事儿1-9合集

    读过《linux那些事儿之我是U盘》的人,都知道其风格,我就不多说了。 导读: linux那些事儿之我是U盘 linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制...

    linux那些事儿之我是USB.zip

    本压缩包文件"linux那些事儿之我是USB.zip"包含了深入理解Linux USB驱动及内核相关知识的九个文档,包括Block层、EHCI主机控制器、HUB、PCI、SCSI硬盘、Sysfs、UHCI、USB core以及U盘。这些文档旨在提供一个系统性的...

    LINUX\Linux那些事儿系列

    3. **Linux那些事儿之我是U盘.pdf**: USB闪存驱动器,通常称为U盘,是常见的便携式存储设备。这部分可能讲述Linux如何识别、挂载和管理U盘,包括使用ums(USB Mass Storage)驱动程序,以及如何处理文件系统的读写...

    Linux那些事儿之全集

    导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux 那些事儿全集

    “Linux那些事儿之我是U盘.pdf”将关注通用串行总线(USB)闪存驱动器。这部分会解释Linux如何识别和处理USB闪存设备,包括文件系统的挂载和数据交换过程。 “Linux那些事儿之我是USB Core.pdf”涉及Linux内核的USB...

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs ...Linux那些事儿之我是U盘

    linux那些事儿之我是U盘

    ### Linux与USB存储设备——《Linux那些事儿之我是U盘》知识点提炼 #### 引言 本文基于一篇幽默且深入的教程《Linux那些事儿之我是U盘》,该文章通过作者的亲身经历和深入浅出的讲解,介绍了Linux操作系统中USB存储...

Global site tag (gtag.js) - Google Analytics