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

接下来,第三座大山是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相关的信息.我们可以从下面这张表中看到,

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 605.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

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

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

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

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

<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 96.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image003.emz"></imagedata></shape>

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

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

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

<shape id="_x0000_i1027" style="WIDTH: 414.75pt; HEIGHT: 108pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image005.emz"></imagedata></shape>

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

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

<shape id="_x0000_i1028" style="WIDTH: 415.5pt; HEIGHT: 162.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image007.emz"></imagedata></shape>

如果你深入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-2Table 101来告诉你.

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

<shape id="_x0000_i1029" style="WIDTH: 414.75pt; HEIGHT: 329.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image009.emz"></imagedata></shape>

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

而接下来,再次根据modepage8还是6来做不同的赋值,我们还是只看主流的情况,即考虑modepage8的情况.buffer[offset+2]就是这里的Byte2.很明显对照这张图来看,我们要的是这里的WCERCD这两个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的值.WCE1说明我们在写操作的时候可以启用cache,即只要写入数据到了cache中就先返回成功,而不用等到真正写到介质中以后再返回.RCD1则说明我们读数据的时候必须从介质中读,而不是从cache中读.

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

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

<shape id="_x0000_i1030" style="WIDTH: 415.5pt; HEIGHT: 63.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image011.emz"></imagedata></shape>

其实这幅图我们似曾相识.之前我们就是通过这个WP位的了解设备是否设置了写保护的.而这里bit4叫做DPOFUA,这一个Bit如果为1.说明设备支持DPOFUA bits,如果为0,说明设备并不支持DPOFUA bits.DPOdisable page out的缩写,FUAforce 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-24.10,专门介绍Cache的一节.如果你的英文和我一样优秀,没有怎么就通过了四六级,那么我建议你认真阅读一下以上的文字,多学习英文有好处,至少等你往某师范学院学报投稿<<为人民服务与党的先进性>>的时候,不会像某作者一样把英文标题取为”Behave the people’s advanced sex of the service and party”,等你向某省经济管理干部学院学报发表<<开拓进取真抓实干不断开创西部大开发的新局面>>的时候,不会像某人把真抓实干翻译成Really Grasp Solid Fuck.

而如果你不想认真看,那么一句话总结,上面这段话说的就是如果你的DPOFUA 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,所以这里我们需要设置DPOFUA0.

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

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

你可能感兴趣的:(linux)