接下来,第三座大山是sd_read_cache_type.

1385 /*

1386  * sd_read_cache_type - called only from sd_revalidate_disk()

1387  * called with buffer of length SD_BUF_SIZE

1388  */

1389 static void

1390 sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)

1391 {

1392         int len = 0, res;

1393         struct scsi_device *sdp = sdkp->device;

1394

1395         int dbd;

1396         int modepage;

1397         struct scsi_mode_data data;

1398         struct scsi_sense_hdr sshdr;

1399

1400         if (sdp->skip_ms_page_8)

1401                 goto defaults;

1402

1403         if (sdp->type == TYPE_RBC) {

1404                 modepage = 6;

1405                 dbd = 8;

1406         } else {

1407                 modepage = 8;

1408                 dbd = 0;

1409         }

1410

1411         /* cautiously ask */

1412         res = sd_do_mode_sense(sdp, dbd, modepage, buffer, 4, &data, &sshdr);

1413

1414         if (!scsi_status_is_good(res))

1415                 goto bad_sense;

1416

1417         if (!data.header_length) {

1418                 modepage = 6;

1419                 sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response/n");

1420         }

1421

1422         /* that went OK, now ask for the proper length */

1423         len = data.length;

1424

1425         /*

1426          * We're only interested in the first three bytes, actually.

1427          * But the data cache page is defined for the first 20.

1428          */

1429         if (len < 3)

1430                 goto bad_sense;

1431         if (len > 20)

1432                 len = 20;

1433

1434         /* Take headers and block descriptors into account */

1435         len += data.header_length + data.block_descriptor_length;

1436         if (len > SD_BUF_SIZE)

1437                 goto bad_sense;

1438

1439         /* Get the data */

1440         res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr);

1441

1442         if (scsi_status_is_good(res)) {

1443                 int offset = data.header_length + data.block_descriptor_length;

1444

1445                 if (offset >= SD_BUF_SIZE - 2) {

1446                         sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response/n");

1447                         goto defaults;

1448                 }

1449

1450                 if ((buffer[offset] & 0x3f) != modepage) {

1451                         sd_printk(KERN_ERR, sdkp, "Got wrong page/n");

1452                         goto defaults;

1453                 }

1454

1455                 if (modepage == 8) {

1456                         sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0);

1457                         sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0);

1458                 } else {

1459                         sdkp->WCE = ((buffer[offset + 2] & 0x01) == 0);

1460                         sdkp->RCD = 0;

1461                 }

1462

1463                 sdkp->DPOFUA = (data.device_specific & 0x10) != 0;

1464                 if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) {

1465                         sd_printk(KERN_NOTICE, sdkp,

1466                                   "Uses READ/WRITE(6), disabling FUA/n");

1467                         sdkp->DPOFUA = 0;

1468                 }

1469

1470                 sd_printk(KERN_NOTICE, sdkp,

1471                        "Write cache: %s, read cache: %s, %s/n",

1472                        sdkp->WCE ? "enabled" : "disabled",

1473                        sdkp->RCD ? "disabled" : "enabled",

1474                        sdkp->DPOFUA ? "supports DPO and FUA"

1475                        : "doesn't support DPO or FUA");

1476

1477                 return;

1478         }

1479

1480 bad_sense:

1481         if (scsi_sense_valid(&sshdr) &&

1482             sshdr.sense_key == ILLEGAL_REQUEST &&

1483             sshdr.asc == 0x24 && sshdr.ascq == 0x0)

1484                 /* Invalid field in CDB */

1485                 sd_printk(KERN_NOTICE, sdkp, "Cache data unavailable/n");

1486         else

1487                 sd_printk(KERN_ERR, sdkp, "Asking for cache data failed/n");

1488

1489 defaults:

1490         sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through/n");

1491         sdkp->WCE = 0;

1492         sdkp->RCD = 0;

1493         sdkp->DPOFUA = 0;

1494 }

很显然,这个函数最主要的工作还是调用sd_do_mode_sense,即还是发送MODE SENSE命令.我们前面说过,SCSI设备写真集最多就是64页(64=0x3f+1).而这里我们给modepage赋值为8,或者对于RBC,赋值为6.这是为什么呢?首先我们必须明确,我们眼下的目的是读取设备写真集中关于Cache的信息,事实上每个SCSI磁盘,或者更有专业精神的说法,每一个Direct-access block device,都可以实现caches,通过使用cache可以提高设备的性能,比如可以减少访问时间,比如可以增加数据吞吐量.而在SBC-2中,为SCSI磁盘定义了一个Mode Page专门用来描述和cache相关的信息.我们可以从下面这张表中看到,

08h这个Page,被叫做caching mode page,这一个Page就是我们需要的.这也就是为什么我们赋值modepage为8.而对于遵循RBC协议的设备这个值会是6,这个我们不去理睬.

下面我们需要理解两个东西.一个是这个Caching Mode page究竟长什么样.另一个是这里我们看到的1443行定义的offset到底表示什么意思?

先看第二个问题.SPC-4中的Table238定义了MODE SENSE命令的返回值的格式:

可以看到这个命令返回值一共有三部分,即Mode Parameter Header,Block Descriptor,Mode Page(s).而Mode Page出现在第三部分.比如我们这里点名要Mode Page 8,那么它就出现在这里的第三部分.首先我们所有的返回值都保存在buffer[]数组中,如果我们要访问Mode Pages这一部分,我们就必须知道前面两个部分的长度.假设前面两个部分的长度为offset,那么我们要访问第三部分就可以使用buffer[offset],这样我们就知道这个offset的含义了.那么前两部分究竟有多长呢?换言之这个offset究竟是多少?

我们先看第一部分是如何定义的,对于6字节的MODE SENSE,

而对于10字节的MODE SENSE命令,这部分稍微复杂些.

如果你深入scsi_mode_sense()函数,你会发现,其实data.header_length恰恰就是这个Mode Parameter Header的长度,而data.block_descriptor_length恰恰就是第二部分的长度,即Block Descriptor的长度.这就是为什么我们会在1443行令offset等于这俩之和.

于是我们用buffer[offset]就定位到了Mode Page这一部分,但是Mode Page具体长什么样呢?或者更直接一点,Caching Mode Page长什么样?让SBC-2的Table 101来告诉你.

我们看到这个Page一共有19个bytes,而我们知道buffer[offset]就应该对应它的Byte0.而这里我们看到Byte0的bit0到bit5代表的就是PAGE CODE,即对于caching mode page来说它应该是08h,这就是为什么我们在1450行要取buffer[offset]的低6位来判断它是否是我们期待的那个08h.如果不是,就说明错了.事实上,任何一个Mode Page的这6位表示的都是Page Code.这个位置就相当于我们在校期间的学号.分辨一个人是不是你要找的人,你可以通过学号去判别.

而接下来,再次根据modepage是8还是6来做不同的赋值,我们还是只看主流的情况,即考虑modepage为8的情况.buffer[offset+2]就是这里的Byte2.很明显对照这张图来看,我们要的是这里的WCE和RCD这两个bits,看看它们是1还是0.那么这两位的含义是什么呢?SBC-2中对这两位是这样描述的.

A writeback cache enable (WCE) bit set to zero specifies that the device server shall return GOOD status for a WRITE command only after successfully writing all of the data to the medium. A WCE bit set to one specifies that the device server may return GOOD status for a WRITE command after successfully receiving the data and prior to having successfully written it to the medium.

A read cache disable (RCD) bit set to zero specifies that the device server may return data requested by a READ command by accessing either the cache or medium. A RCD bit set to one specifies that the device server shall transfer all of the data requested by a READ command from the medium (i.e., data shall not be transferred from the cache).

很显然以上这些词汇基本上都是我们九年制义务教育中学过的.唯一一个例外也许就是cache,记下这两个bit的值对我们之后的工作有用,所以我们费尽周折处心积虑不择手段翻山越岭跋山涉水就是要得到这两个bit的值.WCE为1说明我们在写操作的时候可以启用cache,即只要写入数据到了cache中就先返回成功,而不用等到真正写到介质中以后再返回.RCD为1则说明我们读数据的时候必须从介质中读,而不是从cache中读.

最后,一个叫做DPOFUA的bit也是需要我们牢记心中的.看到这个东西来自data.device_specific,这个东西就是前面那幅Mode Parameter Header中的DEVICE SPECIFIC PARAMETER,对于遵循SBC-2的设备,这一项的格式也是专门有定义的:

其实这幅图我们似曾相识.之前我们就是通过这个WP位的了解设备是否设置了写保护的.而这里bit4叫做DPOFUA,这一个Bit如果为1.说明设备支持DPO和FUA bits,如果为0,说明设备并不支持DPO和FUA bits.DPO是disable page out的缩写,FUA是force unit access的缩写.我知道,一味的复制粘贴是一件很无耻的事情,但是你也不用对我要求太高,因为现在本来就是一个道德沦丧的社会.在这个社会里,偷一个人的主意是剽窃,偷很多人的主意就是研究,所以我只好时而剽窃,时而研究.

When the cache becomes full of logical blocks, new logical blocks may replace those currently in the cache. The disable page out (DPO) bit in the CDB of commands performing write, read, or verify operations allows the application client to influence the replacement of logical blocks in the cache. For write operations, setting the DPO bit to one specifies that the device server should not replace existing logical blocks in the cache with the new logical blocks being written. For read and verify operations, setting the DPO bit to one specifies that the device server should not replace logical blocks in the cache with the logical blocks that are being read.

Application clients may use the force unit access (FUA) bit in the CDB of commands performing write or read operations to specify that the device server shall access the medium. For a write operation, setting the FUA bit to one causes the device server to complete the data write to the medium before completing the command. For a read operation, setting the FUA bit to one causes the device server to retrieve the logical blocks from the medium rather than from the cache.

When the DPO and FUA bits are both set to one, write and read operations effectively bypass the cache.

以上引文来自SBC-2的4.10,专门介绍Cache的一节.如果你的英文和我一样优秀,没有怎么作弊就通过了四六级,那么我建议你认真阅读一下以上的文字,多学习英文有好处,至少等你往某师范学院学报投稿<<为人民服务与党的先进性>>的时候,不会像某作者一样把英文标题取为”Behave the people’s advanced sex of the service and party”,等你向某省经济管理干部学院学报发表<<开拓进取真抓实干不断开创西部大开发的新局面>>的时候,不会像某人把真抓实干翻译成Really Grasp Solid Fuck.

而如果你不想认真看,那么一句话总结,上面这段话说的就是如果你的DPO和FUA bits被设置成了1,那么你的读写操作都不会使用cache.因为DPOFUA是这里的bit4,所以我们看到1463行,data.device_specific就是和0x10相与,广州飞车党的兄弟们都知道,这样得到的就是bit4.

不过,和MODE SENSE命令一样,READ/WRITE命令也有6字节和10字节的,对于READ/WRITE操作,默认情况下咱们会先尝试使用10字节的.但是咱们也允许你违反游戏规则.struct scsi_device结构体中unsigned use_10_for_rw,就是你可以设置的.默认情况下,咱们会在设备初始化的时候,确切的说,在scsi总线扫描的时候,scsi_add_lun函数中会把这个flag设置为1.但如果你偏要特立独行,那也随便你.但是实际上6字节的READ/Write命令中没有定义FUA,DPO,所以这里我们需要设置DPOFUA为0.

最后,带着sdkp->WCE,sdkp->RCD,sdkp->DPOFUA,sd_read_cache_type()函数满载而归.

翻过了三座大山,我们回到了sd_revalidate_disk.

Linux那些事儿之我是SCSI硬盘(6)三座大山(三)相关推荐

  1. Linux那些事儿之我是SCSI硬盘(3)磁盘磁盘你动起来!

    首先我们看sd_revalidate_disk(),这个函数很重要,一定程度上来说,正是这个函数从硬件和软件两个方面掀起了我们了解scsi磁盘的性高潮.这个函数它不是一个函数在战斗,它完全是贾宝玉林黛 ...

  2. Linux设备之我是usb,linux那些事儿之我是usb

    linux那些事儿之我是usb,复旦大学教授肖林甫先生给学生们解说的linux操作系统的一些硬件驱动开发的事儿. 内核说明: 我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内 ...

  3. 《Linux那些事儿之我是USB》我是U盘(1)小城故事

    这个故事中使用的是2.6.22的内核代码.在Linux内核代码目录中,所有与设备驱动程序有关的代码都在drivers/目录下面,在这个目录中用ls命令可以看到很多子目录: lfg1:/usr/src/ ...

  4. Linux系统USB驱动目录,Linux那些事儿之我是USB 目录

    目录 第1篇  Linux那些事儿之我是USB Core 1.引子 2 2.它从哪里来 2 3.PK 3 4.漫漫辛酸路 3 5.我型我秀 4 6.我是一棵树 5 7.我是谁 9 8.好戏开始了 11 ...

  5. linux 那些事儿之我是 u 盘,《Linux那些事儿之我是USB》.PDF

    <Linux 那些事儿之我是 USB> 作者:华清远见 第 1 章 Linux 那些事儿之我是 USB Core 专业始于专注 卓识源于远见 1 .引子 老夫子们痛心疾首地总结说,现代青年 ...

  6. 读书笔记《Linux那些事儿之我是USB》

    第一篇:Linux那些事儿之我是USB Core USB诞生于inel 产生是为了解决前期计算机并口串口的问题,实现一种解决速度,扩展性,易用性的通信方式. 速度:usb2.0高速模式,480MB/s ...

  7. Linux那些事儿之我是Hub(9)While You Were Sleeping(二)

    老实说,从函数一个开始的598行直到627行都没有什么可说的.其中需要一提的是,606行,调用usb_buffer_alloc()申请内存,赋给hub->buffer.614行,调用kmallo ...

  8. 《Linux那些事儿之我是USB》我是U盘(4)想到达明天现在就要启程

    既然知道了编写模块的方法,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块的人都不会觉得写设备驱动有困难. 真的,我没说假话,写驱动不是什么难事,你完全可以很自信地说,你已经可以写 设备驱动 ...

  9. Linux那些事儿之我是U盘(51)光荣属于苹果,属于诺基亚,属于摩托罗拉,属于索尼爱立信

    这一节我们来分析一个在很多企业的产品中都存在的bug.写设备驱动是一件很实在的事情,你得根据实实在在的硬件来编写你的代码,如果你的硬件存在某种bug,那么你就要去fix它.如果你希望成为通用的驱动程序 ...

最新文章

  1. iOS cell 里需要创建n个元素
  2. python开发环境spyder_spyder python 2.7-spyder(Python开发环境)下载 v2.1.10官方版--pc6下载站...
  3. Linux 添加DNS配置
  4. 音视频技术开发周刊 | 226
  5. 【汇编语言】状态标志符(CF/OF/SF/ZF)在运算(ADD/SUB/ADC/SBB)过程中的响应变化
  6. 程序员的大学|彪悍的人生可以没有妹,但必须要有技术!
  7. 此上下文中不允许函数定义。_DAX函数---ALL家族
  8. nginx unit
  9. django 1.8 官方文档翻译: 3-6-1 中间件概览
  10. Java数组学习笔记(遍历、排序、多维数组、命令行参数)
  11. php 百度报表工具下载,PHP Report Maker12最新版
  12. 重磅炸弹!马云正式宣布:1天1个亿,全世界沸腾了!
  13. 正则表达式中符号的含义(可能不是很全)
  14. drawroundrect java_java – fillRoundRect看似无法正确呈现的问题
  15. TCP/IP网络编程-前三章学习笔记
  16. 三维人体姿态估计年度进展综述(周晓巍教授)
  17. 日本酒店寄送行李至机场的攻略
  18. 黑客防线2012合订本
  19. C++畅玩五子棋项目
  20. 鸿蒙HarmonyOS关于NFC碰一碰拉起FA

热门文章

  1. TikTok网红营销:出海品牌如何借势2022圣诞节?
  2. elementui-button没写disabled属性自动加入disabled属性(记录)
  3. 《机器学习在线 解析阿里云机器学习平台》读书笔记
  4. 洛谷P4768 kruskal重构树
  5. MySQL自动备份到本地数据库,服务器上的Mysql数据自动备份到本地
  6. 苹果自带拼音转换方法
  7. exchange设置收发邮件大小
  8. 书论50 宋高宗《翰墨志》
  9. PHP投票系统如何防刷票
  10. 7-3 sdut-sel-while-1 社会主义核心价值观之理解与输出 (20 分)