Write Cache这个概念对于大家应该不陌生,主要是机械HDD中会有很明显的效果。
-
Write Cache Enable,WCE:数据不会直接落盘,而是写入DRAM缓存后就直接返回了,对于随机写和顺序写的性能都会有所改善。这个场景,对于数据保护的要求可能存在一定的风险,如果数据在cache中还未刷入落盘,掉电数据也会丢失
-
Write Cache Disable,WCD:数据需要完全落入碟片后才算写入成功,性能会有下降,但是数据安全是可靠的,即使断电,数据可以保证落盘。
小编曾经在研究HBA-Expander-HDD架构性能相关问题时遇到过一个有趣的现象,在linux系统下用两个工具:sdparm和hdparm去修改HDD的write cache,发现在系统下write cache设置的效果有差异。
按照官网(https://wiki.archlinux.org/title/Hdparm)的定义,两个工具都是针对sata/sas盘进行参数读取的工具,而且相辅相成,是对好兄弟,为何在设置write cache过程表现的不一样呢?这个就是本文主要的趣事分解。

首先,我们先来看hdparm修改write cache的方式和原理。
查看write cache的状态:hdparm -W /dev/sdX
打开write cache:hdparm -W 1 /dev/sdX
关闭write cache:hdparm -W 0 /dev/sdX
hdparm源码中设置write cache的代码:
/* prototypes and stuff for ATA command ioctls */#include <linux/types.h>enum {...ATA_OP_SETFEATURES = 0xef,...};if (set_wcache) {if (get_wcache) {printf(" setting drive write-caching to %d", wcache);on_off(wcache);}if (!wcache)err = flush_wcache(fd);if (ioctl(fd, HDIO_SET_WCACHE, wcache)) {__u8 setcache[4] = {ATA_OP_SETFEATURES,0,0,0}; #通过ATA set feature命令完成设置setcache[2] = wcache ? 0x02 : 0x82;if (do_drive_cmd(fd, setcache, 0)) {err = errno;perror(" HDIO_DRIVE_CMD(setcache) failed");}}if (!wcache)err = flush_wcache(fd);}
基于上面的代码,我们也可以看到hdparm通过kernel libata ATA set feature命令完成设置与硬盘完成交互,最终完成write cache的设置。
那么,sdparm又是怎么修改WCE的呢?
打开write cache:sdparm --set=WCE --save /dev/sdX
关闭write cache:sdparm --clear=WCE --save /dev/sdX
sdparm源码中设置write cache的代码:
for (k = 0; k < num_devices; ++k) {r = 0;pdt = -1;if (op->verbose > 1)pr2serr(">>> about to open device name: %s\n",device_name_arr[k]);sg_fd = open_and_simple_inquiry(device_name_arr[k], rw, &pdt,&protect, op);if (sg_fd < 0) {if (0 == ret)ret = -sg_fd;continue;}if (op->inquiry) {if (op->examine)r = examine_vpd_page(sg_fd, pn, spn, op, req_pdt, protect);elser = sdp_process_vpd_page(sg_fd, pn, ((spn < 0) ? 0: spn), op,req_pdt, protect, NULL, 0, NULL, 0);} else {if (cmd_str && scmdp) /* process command */r = sdp_process_cmd(sg_fd, scmdp, cmd_arg, pdt, op);else { /* mode page */if (op->examine)r = examine_mode_page(sg_fd, pn, op, req_pdt);elser = process_mode_page(sg_fd, &mp_settings, pn, spn,set_clear, (NULL != get_str), op,pdt);}}
基于上面的代码,我们也可以看到sdparm基于SCSI协议交互,通过SCSI mode pages, 实现write cache设置。

通过上面代码,我们可以看到hdparm和sdparm命令走的通道不完全一样,结合linux storage stack看可能会更加容易对比理解:
-
hdparm走的是libata的通道。也就是在HBA+Expander的架构中,在系统通过hdparm修改write cache时,HBA和expander都不会感知write cache的设置,设置write cache的命令会直接透传HDD。
-
sdparm走的是SCSI+mpt3sas驱动的通道。当系统需要修改write cache时,会先经过SATL转换层翻译成ATA命令,这时HBA和expander都会感知write cache的设置。



















