]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/disk.c
1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
11 #include <sys/types.h>
12 #include <sys/statvfs.h>
16 #ifdef HAVE_HDIO_GETGEO
17 # include <linux/hdreg.h>
19 #include "platform_defs.h"
22 #include "xfs_scrub.h"
27 # define BLKROTATIONAL _IO(0x12, 126)
33 * These routines help us to discover the geometry of a block device,
34 * estimate the amount of concurrent IOs that we can send to it, and
35 * abstract the process of performing read verification of disk blocks.
38 /* Figure out how many disk heads are available. */
48 /* If it's not a block device, throw all the CPUs at it. */
49 if (!S_ISBLK(disk
->d_sb
.st_mode
))
52 /* Non-rotational device? Throw all the CPUs at the problem. */
54 error
= ioctl(disk
->d_fd
, BLKROTATIONAL
, &rot
);
55 if (error
== 0 && rot
== 0)
59 * Sometimes we can infer the number of devices from the
60 * min/optimal IO sizes.
63 if (ioctl(disk
->d_fd
, BLKIOMIN
, &iomin
) == 0 &&
64 ioctl(disk
->d_fd
, BLKIOOPT
, &ioopt
) == 0 &&
65 iomin
> 0 && ioopt
> 0) {
66 return min(nproc
, max(1, ioopt
/ iomin
));
69 /* Rotating device? I guess? */
73 /* Figure out how many disk heads are available. */
80 return __disk_heads(disk
);
84 * Execute a SCSI VERIFY(16) to verify disk contents.
85 * For devices that support this command, this can sharply reduce the
86 * runtime of the data block verification phase if the storage device's
87 * internal bandwidth exceeds its link bandwidth. However, it only
88 * works if we're talking to a raw SCSI device, and only if we trust the
92 # define SENSE_BUF_LEN 64
93 # define VERIFY16_CMDLEN 16
94 # define VERIFY16_CMD 0x8F
96 # ifndef SG_FLAG_Q_AT_TAIL
97 # define SG_FLAG_Q_AT_TAIL 0x10
102 uint64_t startblock
, /* lba */
103 uint64_t blockcount
) /* lba */
105 struct sg_io_hdr iohdr
;
106 unsigned char cdb
[VERIFY16_CMDLEN
];
107 unsigned char sense
[SENSE_BUF_LEN
];
109 uint64_t veri_len
= blockcount
;
112 assert(!debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"));
114 llba
= startblock
+ (disk
->d_start
>> BBSHIFT
);
116 /* Borrowed from sg_verify */
117 cdb
[0] = VERIFY16_CMD
;
118 cdb
[1] = 0; /* skip PI, DPO, and byte check. */
119 cdb
[2] = (llba
>> 56) & 0xff;
120 cdb
[3] = (llba
>> 48) & 0xff;
121 cdb
[4] = (llba
>> 40) & 0xff;
122 cdb
[5] = (llba
>> 32) & 0xff;
123 cdb
[6] = (llba
>> 24) & 0xff;
124 cdb
[7] = (llba
>> 16) & 0xff;
125 cdb
[8] = (llba
>> 8) & 0xff;
126 cdb
[9] = llba
& 0xff;
127 cdb
[10] = (veri_len
>> 24) & 0xff;
128 cdb
[11] = (veri_len
>> 16) & 0xff;
129 cdb
[12] = (veri_len
>> 8) & 0xff;
130 cdb
[13] = veri_len
& 0xff;
133 memset(sense
, 0, SENSE_BUF_LEN
);
136 memset(&iohdr
, 0, sizeof(iohdr
));
137 iohdr
.interface_id
= 'S';
138 iohdr
.dxfer_direction
= SG_DXFER_NONE
;
140 iohdr
.cmd_len
= VERIFY16_CMDLEN
;
142 iohdr
.mx_sb_len
= SENSE_BUF_LEN
;
143 iohdr
.flags
|= SG_FLAG_Q_AT_TAIL
;
144 iohdr
.timeout
= 30000; /* 30s */
146 error
= ioctl(disk
->d_fd
, SG_IO
, &iohdr
);
150 dbg_printf("VERIFY(16) fd %d lba %"PRIu64
" len %"PRIu64
" info %x "
151 "status %d masked %d msg %d host %d driver %d "
152 "duration %d resid %d\n",
153 disk
->d_fd
, startblock
, blockcount
, iohdr
.info
,
154 iohdr
.status
, iohdr
.masked_status
, iohdr
.msg_status
,
155 iohdr
.host_status
, iohdr
.driver_status
, iohdr
.duration
,
158 if (iohdr
.info
& SG_INFO_CHECK
) {
159 dbg_printf("status: msg %x host %x driver %x\n",
160 iohdr
.msg_status
, iohdr
.host_status
,
161 iohdr
.driver_status
);
169 # define disk_scsi_verify(...) (ENOTTY)
170 #endif /* HAVE_SG_IO */
172 /* Test the availability of the kernel scrub ioctl. */
174 disk_can_scsi_verify(
179 if (debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"))
182 error
= disk_scsi_verify(disk
, 0, 1);
186 /* Open a disk device and discover its geometry. */
189 const char *pathname
)
191 #ifdef HAVE_HDIO_GETGEO
192 struct hd_geometry bdgeo
;
195 bool suspicious_disk
= false;
199 disk
= calloc(1, sizeof(struct disk
));
203 disk
->d_fd
= open(pathname
, O_RDONLY
| O_DIRECT
| O_NOATIME
);
207 /* Try to get LBA size. */
208 error
= ioctl(disk
->d_fd
, BLKSSZGET
, &lba_sz
);
211 disk
->d_lbalog
= log2_roundup(lba_sz
);
213 /* Obtain disk's stat info. */
214 error
= fstat(disk
->d_fd
, &disk
->d_sb
);
218 /* Determine bdev size, block size, and offset. */
219 if (S_ISBLK(disk
->d_sb
.st_mode
)) {
220 error
= ioctl(disk
->d_fd
, BLKGETSIZE64
, &disk
->d_size
);
223 error
= ioctl(disk
->d_fd
, BLKBSZGET
, &disk
->d_blksize
);
226 #ifdef HAVE_HDIO_GETGEO
227 error
= ioctl(disk
->d_fd
, HDIO_GETGEO
, &bdgeo
);
230 * dm devices will pass through ioctls, which means
231 * we can't use SCSI VERIFY unless the start is 0.
232 * Most dm devices don't set geometry (unlike scsi
233 * and nvme) so use a zeroed out CHS to screen them
236 if (bdgeo
.start
!= 0 &&
237 (unsigned long long)bdgeo
.heads
* bdgeo
.sectors
*
239 suspicious_disk
= true;
240 disk
->d_start
= bdgeo
.start
<< BBSHIFT
;
245 disk
->d_size
= disk
->d_sb
.st_size
;
246 disk
->d_blksize
= disk
->d_sb
.st_blksize
;
250 /* Can we issue SCSI VERIFY? */
251 if (!suspicious_disk
&& disk_can_scsi_verify(disk
))
252 disk
->d_flags
|= DISK_FLAG_SCSI_VERIFY
;
262 /* Close a disk device. */
270 error
= close(disk
->d_fd
);
276 #define BTOLBAT(d, bytes) ((uint64_t)(bytes) >> (d)->d_lbalog)
277 #define LBASIZE(d) (1ULL << (d)->d_lbalog)
278 #define BTOLBA(d, bytes) (((uint64_t)(bytes) + LBASIZE(d) - 1) >> (d)->d_lbalog)
280 /* Read-verify an extent of a disk device. */
288 /* Convert to logical block size. */
289 if (disk
->d_flags
& DISK_FLAG_SCSI_VERIFY
)
290 return disk_scsi_verify(disk
, BTOLBAT(disk
, start
),
291 BTOLBA(disk
, length
));
293 return pread(disk
->d_fd
, buf
, length
, start
);