[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 1/2] file-posix: Correctly read max_segments of SG nodes
From: |
Dmitry Fomichev |
Subject: |
[PATCH 1/2] file-posix: Correctly read max_segments of SG nodes |
Date: |
Wed, 12 Aug 2020 07:51:21 +0900 |
If scsi-generic driver is in use, an SG node can be specified in
the command line in place of a regular SCSI device. In this case,
sg_get_max_segments() fails to open max_segments entry in sysfs
because /dev/sgX is a character device. As the result, the maximum
transfer size for the device may be calculated incorrectly, causing
I/O errors if the maximum transfer size at the guest ends up to be
larger compared to the host.
Check system device type in sg_get_max_segments() and read the
max_segments value differently if it is a character device.
Reported-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Fixes: 9103f1ceb46614b150bcbc3c9a4fbc72b47fedcc
Signed-off-by: Dmitry Fomichev <dmitry.fomichev@wdc.com>
---
block/file-posix.c | 55 +++++++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 23 deletions(-)
diff --git a/block/file-posix.c b/block/file-posix.c
index 094e3b0212..f9e2424e8f 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1108,6 +1108,7 @@ static int sg_get_max_segments(int fd)
int ret;
int sysfd = -1;
long max_segments;
+ unsigned int max_segs;
struct stat st;
if (fstat(fd, &st)) {
@@ -1115,30 +1116,38 @@ static int sg_get_max_segments(int fd)
goto out;
}
- sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/max_segments",
- major(st.st_rdev), minor(st.st_rdev));
- sysfd = open(sysfspath, O_RDONLY);
- if (sysfd == -1) {
- ret = -errno;
- goto out;
+ if (S_ISBLK(st.st_mode)) {
+ sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/max_segments",
+ major(st.st_rdev), minor(st.st_rdev));
+ sysfd = open(sysfspath, O_RDONLY);
+ if (sysfd == -1) {
+ ret = -errno;
+ goto out;
+ }
+ do {
+ ret = read(sysfd, buf, sizeof(buf) - 1);
+ } while (ret == -1 && errno == EINTR);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ } else if (ret == 0) {
+ ret = -EIO;
+ goto out;
+ }
+ buf[ret] = 0;
+ /* The file is ended with '\n', pass 'end' to accept that. */
+ ret = qemu_strtol(buf, &end, 10, &max_segments);
+ if (ret == 0 && end && *end == '\n') {
+ ret = max_segments;
+ }
+ } else {
+ ret = ioctl(fd, SG_GET_SG_TABLESIZE, &max_segs);
+ if (ret != 0) {
+ ret = -errno;
+ goto out;
+ }
+ ret = max_segs;
}
- do {
- ret = read(sysfd, buf, sizeof(buf) - 1);
- } while (ret == -1 && errno == EINTR);
- if (ret < 0) {
- ret = -errno;
- goto out;
- } else if (ret == 0) {
- ret = -EIO;
- goto out;
- }
- buf[ret] = 0;
- /* The file is ended with '\n', pass 'end' to accept that. */
- ret = qemu_strtol(buf, &end, 10, &max_segments);
- if (ret == 0 && end && *end == '\n') {
- ret = max_segments;
- }
-
out:
if (sysfd != -1) {
close(sysfd);
--
2.21.0