hardware_TrimIntegrity.py revision 79a010971e2d9c053130202a2ad35eca4081762a
1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging, os, fcntl, struct, random 6 7from autotest_lib.client.bin import test, utils 8from autotest_lib.client.common_lib import error 9 10 11class hardware_TrimIntegrity(test.test): 12 """ 13 Performs data integrity trim test on an unmounted partition. 14 15 This test will write 1 GB of data and verify that trimmed data are gone and 16 untrimmed data are unaffected. The verification will be run in 5 passes with 17 0%, 25%, 50%, 75%, and 100% of data trimmed. 18 """ 19 20 version = 1 21 FILE_SIZE = 1024 * 1024 * 1024 22 CHUNK_SIZE = 64 * 1024 23 TRIM_RATIO = [0, 0.25, 0.5, 0.75, 1] 24 25 # Use hash value to check integrity of the random data. 26 HASH_CMD = 'sha256sum | cut -d" " -f 1' 27 # 0x1277 is ioctl BLKDISCARD command 28 IOCTL_TRIM_CMD = 0x1277 29 IOCTL_NOT_SUPPORT_ERRNO = 95 30 31 def _find_free_root_partition(self): 32 """ 33 Locate the spare root partition that we didn't boot off. 34 """ 35 36 spare_root_map = { 37 '3': '5', 38 '5': '3', 39 } 40 rootdev = utils.system_output('rootdev -s') 41 spare_root = rootdev[:-1] + spare_root_map[rootdev[-1]] 42 self._filename = spare_root 43 44 def _get_hash(self, chunk_count, chunk_size): 45 """ 46 Get hash for every chunk of data. 47 """ 48 cmd = str('for i in $(seq 0 %d); do dd if=%s of=/dev/stdout bs=%d' 49 ' count=1 skip=$i iflag=direct | %s; done' % 50 (chunk_count - 1, self._filename, chunk_size, self.HASH_CMD)) 51 return utils.run(cmd).stdout.split() 52 53 def _do_trim(self, fd, offset, size): 54 """ 55 Invoke ioctl to trim command. 56 """ 57 fcntl.ioctl(fd, self.IOCTL_TRIM_CMD, struct.pack('QQ', offset, size)) 58 59 def run_once(self, file_size=FILE_SIZE, chunk_size=CHUNK_SIZE, 60 trim_ratio=TRIM_RATIO): 61 """ 62 Executes the test and logs the output. 63 """ 64 65 self._find_free_root_partition() 66 67 # Check for trim support in ioctl. Gracefully exit if not support. 68 try: 69 fd = os.open(self._filename, os.O_RDWR, 0666) 70 self._do_trim(fd, 0, chunk_size) 71 except IOError, err: 72 if err.errno == self.IOCTL_NOT_SUPPORT_ERRNO: 73 logging.info("IOCTL Does not support trim.") 74 return 0 75 else: 76 raise 77 finally: 78 os.close(fd) 79 80 # Write random data to disk 81 chunk_count = file_size / chunk_size 82 cmd = str('dd if=/dev/urandom of=%s bs=%d count=%d oflag=direct' % 83 (self._filename, chunk_size, chunk_count)) 84 utils.run(cmd) 85 86 # Calculate hash value for zero'ed and one'ed data 87 cmd = str('dd if=/dev/zero of=/dev/stdout bs=%d count=1 | %s' % 88 (chunk_size, self.HASH_CMD)) 89 zero_hash = utils.run(cmd).stdout.strip() 90 91 cmd = str('dd if=/dev/ibe of=/dev/stdout bs=%d count=1 | %s' % 92 (chunk_size, self.HASH_CMD)) 93 one_hash = utils.run(cmd).stdout.strip() 94 95 trim_hash = "" 96 97 ref_hash = self._get_hash(chunk_count, chunk_size) 98 99 # Generate random order of chunk to trim 100 trim_order = list(range(0, chunk_count)) 101 random.shuffle(trim_order) 102 trim_status = [False] * chunk_size 103 104 # Init stat variable 105 data_verify_count = 0 106 data_verify_match = 0 107 trim_verify_count = 0 108 trim_verify_zero = 0 109 trim_verify_one = 0 110 trim_verify_non_delete = 0 111 trim_deterministic = True 112 113 last_ratio = 0 114 for ratio in trim_ratio: 115 116 # Do trim 117 begin_trim_chunk = int(last_ratio * chunk_count) 118 end_trim_chunk = int(ratio * chunk_count) 119 fd = os.open(self._filename, os.O_RDWR, 0666) 120 for chunk in trim_order[begin_trim_chunk:end_trim_chunk]: 121 self._do_trim(fd, chunk * chunk_size, chunk_size) 122 trim_status[chunk] = True 123 os.close(fd) 124 last_ratio = ratio 125 126 cur_hash = self._get_hash(chunk_count, chunk_size) 127 128 trim_verify_count += int(ratio * chunk_count) 129 data_verify_count += chunk_count - int(ratio * chunk_count) 130 131 # Verify hash 132 for cur, ref, trim in zip(cur_hash, ref_hash, trim_status): 133 if trim: 134 if not trim_hash: 135 trim_hash = cur 136 elif cur != trim_hash: 137 trim_deterministic = False 138 139 if cur == zero_hash: 140 trim_verify_zero += 1 141 elif cur == one_hash: 142 trim_verify_one += 1 143 elif cur == ref: 144 trim_verify_non_delete += 1 145 else: 146 if cur == ref: 147 data_verify_match += 1 148 149 keyval = dict() 150 keyval['data_verify_count'] = data_verify_count 151 keyval['data_verify_match'] = data_verify_match 152 keyval['trim_verify_count'] = trim_verify_count 153 keyval['trim_verify_zero'] = trim_verify_zero 154 keyval['trim_verify_one'] = trim_verify_one 155 keyval['trim_verify_non_delete'] = trim_verify_non_delete 156 keyval['trim_deterministic'] = trim_deterministic 157 self.write_perf_keyval(keyval) 158 159 # Raise error when untrimmed data changed only. 160 # Don't care about trimmed data. 161 if data_verify_match < data_verify_count: 162 error.testFail("Fail to verify untrimmed data.") 163 if trim_verify_non_delete > 0 : 164 error.testFail("Trimmed data are not deleted.") 165