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); else r = 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);
else r = 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的设置。