On 08/03/2017 08:01, Fam Zheng wrote: > Linux exposes a separate limit, /sys/block/.../queue/max_segments, which > in the worst case can be more restrictive than BLKSECTGET (as they are > two different things). Similar to the BLKSECTGET story, guests don't see > this limit and send big requests will get -EINVAL error on SG_IO. > > Lean on the safer side to clamp max_transfer according to max_segments > and page size, because in the end what host HBA gets is the mapped host > pages rather than a guest buffer. > > Signed-off-by: Fam Zheng <f...@redhat.com> > > --- > > v2: Use /sys/dev/block/MAJOR:MINOR/queue/max_segments. [Paolo] > --- > block/file-posix.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 47 insertions(+) > > diff --git a/block/file-posix.c b/block/file-posix.c > index 4de1abd..c4c0663 100644 > --- a/block/file-posix.c > +++ b/block/file-posix.c > @@ -668,6 +668,48 @@ static int hdev_get_max_transfer_length(BlockDriverState > *bs, int fd) > #endif > } > > +static int hdev_get_max_segments(const struct stat *st) > +{ > +#ifdef CONFIG_LINUX > + char buf[32]; > + const char *end; > + char *sysfspath; > + int ret; > + int fd = -1; > + long max_segments; > + > + sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/max_segments", > + major(st->st_rdev), minor(st->st_rdev)); > + fd = open(sysfspath, O_RDONLY); > + if (fd == -1) { > + ret = -errno; > + goto out; > + } > + do { > + ret = read(fd, buf, sizeof(buf)); > + } 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: > + g_free(sysfspath); > + return ret; > +#else > + return -ENOTSUP; > +#endif > +} > + > static void raw_refresh_limits(BlockDriverState *bs, Error **errp) > { > BDRVRawState *s = bs->opaque; > @@ -679,6 +721,11 @@ static void raw_refresh_limits(BlockDriverState *bs, > Error **errp) > if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) { > bs->bl.max_transfer = pow2floor(ret); > } > + ret = hdev_get_max_segments(&st); > + if (ret > 0) { > + bs->bl.max_transfer = MIN(bs->bl.max_transfer, > + ret * getpagesize()); > + } > } > } > >
Reviewed-by: Paolo Bonzini <pbonz...@redhat.com> Thanks!