#include <fcntl.h>
#include <sys/types.h>
#include <sys/statvfs.h>
-#include <scsi/sg.h>
-#include <linux/hdreg.h>
#include "platform_defs.h"
#include "libfrog/util.h"
#include "libfrog/paths.h"
return __disk_heads(disk);
}
-/*
- * Execute a SCSI VERIFY(16) to verify disk contents.
- * For devices that support this command, this can sharply reduce the
- * runtime of the data block verification phase if the storage device's
- * internal bandwidth exceeds its link bandwidth. However, it only
- * works if we're talking to a raw SCSI device, and only if we trust the
- * firmware.
- */
-#define SENSE_BUF_LEN 64
-#define VERIFY16_CMDLEN 16
-#define VERIFY16_CMD 0x8F
-
-#ifndef SG_FLAG_Q_AT_TAIL
-# define SG_FLAG_Q_AT_TAIL 0x10
-#endif
-static int
-disk_scsi_verify(
- struct disk *disk,
- uint64_t startblock, /* lba */
- uint64_t blockcount) /* lba */
-{
- struct sg_io_hdr iohdr;
- unsigned char cdb[VERIFY16_CMDLEN];
- unsigned char sense[SENSE_BUF_LEN];
- uint64_t llba;
- uint64_t veri_len = blockcount;
- int error;
-
- assert(!debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"));
-
- llba = startblock + (disk->d_start >> BBSHIFT);
-
- /* Borrowed from sg_verify */
- cdb[0] = VERIFY16_CMD;
- cdb[1] = 0; /* skip PI, DPO, and byte check. */
- cdb[2] = (llba >> 56) & 0xff;
- cdb[3] = (llba >> 48) & 0xff;
- cdb[4] = (llba >> 40) & 0xff;
- cdb[5] = (llba >> 32) & 0xff;
- cdb[6] = (llba >> 24) & 0xff;
- cdb[7] = (llba >> 16) & 0xff;
- cdb[8] = (llba >> 8) & 0xff;
- cdb[9] = llba & 0xff;
- cdb[10] = (veri_len >> 24) & 0xff;
- cdb[11] = (veri_len >> 16) & 0xff;
- cdb[12] = (veri_len >> 8) & 0xff;
- cdb[13] = veri_len & 0xff;
- cdb[14] = 0;
- cdb[15] = 0;
- memset(sense, 0, SENSE_BUF_LEN);
-
- /* v3 SG_IO */
- memset(&iohdr, 0, sizeof(iohdr));
- iohdr.interface_id = 'S';
- iohdr.dxfer_direction = SG_DXFER_NONE;
- iohdr.cmdp = cdb;
- iohdr.cmd_len = VERIFY16_CMDLEN;
- iohdr.sbp = sense;
- iohdr.mx_sb_len = SENSE_BUF_LEN;
- iohdr.flags |= SG_FLAG_Q_AT_TAIL;
- iohdr.timeout = 30000; /* 30s */
-
- error = ioctl(disk->d_fd, SG_IO, &iohdr);
- if (error < 0)
- return error;
-
- dbg_printf("VERIFY(16) fd %d lba %"PRIu64" len %"PRIu64" info %x "
- "status %d masked %d msg %d host %d driver %d "
- "duration %d resid %d\n",
- disk->d_fd, startblock, blockcount, iohdr.info,
- iohdr.status, iohdr.masked_status, iohdr.msg_status,
- iohdr.host_status, iohdr.driver_status, iohdr.duration,
- iohdr.resid);
-
- if (iohdr.info & SG_INFO_CHECK) {
- dbg_printf("status: msg %x host %x driver %x\n",
- iohdr.msg_status, iohdr.host_status,
- iohdr.driver_status);
- errno = EIO;
- return -1;
- }
-
- return blockcount << BBSHIFT;
-}
-
-/* Test the availability of the kernel scrub ioctl. */
-static bool
-disk_can_scsi_verify(
- struct disk *disk)
-{
- int error;
-
- if (debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"))
- return false;
-
- error = disk_scsi_verify(disk, 0, 1);
- return error == 0;
-}
-
/* Open a disk device and discover its geometry. */
struct disk *
disk_open(
const char *pathname)
{
- struct hd_geometry bdgeo;
struct disk *disk;
- bool suspicious_disk = false;
int error;
disk = calloc(1, sizeof(struct disk));
error = ioctl(disk->d_fd, BLKBSZGET, &disk->d_blksize);
if (error)
disk->d_blksize = 0;
- error = ioctl(disk->d_fd, HDIO_GETGEO, &bdgeo);
- if (!error) {
- /*
- * dm devices will pass through ioctls, which means
- * we can't use SCSI VERIFY unless the start is 0.
- * Most dm devices don't set geometry (unlike scsi
- * and nvme) so use a zeroed out CHS to screen them
- * out.
- */
- if (bdgeo.start != 0 &&
- (unsigned long long)bdgeo.heads * bdgeo.sectors *
- bdgeo.sectors == 0)
- suspicious_disk = true;
- disk->d_start = bdgeo.start << BBSHIFT;
- } else
- disk->d_start = 0;
} else {
disk->d_size = disk->d_sb.st_size;
disk->d_blksize = disk->d_sb.st_blksize;
- disk->d_start = 0;
}
- /* Can we issue SCSI VERIFY? */
- if (!suspicious_disk && disk_can_scsi_verify(disk))
- disk->d_flags |= DISK_FLAG_SCSI_VERIFY;
-
return disk;
out_close:
close(disk->d_fd);
return error;
}
-#define BTOLBAT(d, bytes) ((uint64_t)(bytes) >> (d)->d_lbalog)
-#define LBASIZE(d) (1ULL << (d)->d_lbalog)
-#define BTOLBA(d, bytes) (((uint64_t)(bytes) + LBASIZE(d) - 1) >> (d)->d_lbalog)
-
/* Read-verify an extent of a disk device. */
ssize_t
disk_read_verify(
uint64_t start,
uint64_t length)
{
- /* Convert to logical block size. */
- if (disk->d_flags & DISK_FLAG_SCSI_VERIFY)
- return disk_scsi_verify(disk, BTOLBAT(disk, start),
- BTOLBA(disk, length));
-
return pread(disk->d_fd, buf, length, start);
}