From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202410 header.b=X2yaXbyI; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id BEA0A5A061A for ; Wed, 06 Nov 2024 03:39:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202410; t=1730860737; bh=VCrGMMKNYA3cyJcrc+aGOvN7HQIc30JtMC6deBPpEQ4=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=X2yaXbyIFaUQH64wz5nKcTsMbBdwKLquWuE7841cvUjecXk/xaowWnp8ZIEuptxFb 8ZMbpgWIIkU4bp8K8LzxsXISdAsa6ktTKnoWVNszOtRlFHB7A7o/gQvmQR01Tz+eaD WyZXpTdrz2iXUEdxwjDQVb8jEEDvm0JqrSMqy19PIcSXcGWBZWrn5V83j7ln4X0POW +FjTBMM0I2tLPiHOWfFlLKG6SsURU6EPPKrHqNQ7snJ/6BMvc92QOfc2etZjuJw70W /l+Fm2IYgyIJengIKT+qGKi2sgDrjda5u/tGBz/PYxC0qK6Ax7FDk1CcO97w4z3j8U DE97lTCCXmg+Q== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4XjqCP33HKz4x7D; Wed, 6 Nov 2024 13:38:57 +1100 (AEDT) Date: Wed, 6 Nov 2024 13:38:38 +1100 From: David Gibson To: Stefano Brivio Subject: Re: [PATCH 1/1] iov: iov tail helpers Message-ID: References: <20241105023222.698658-1-david@gibson.dropbear.id.au> <20241105023222.698658-2-david@gibson.dropbear.id.au> <20241106015631.53041587@elisabeth> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="ZFGdwF1r9pc/fTmO" Content-Disposition: inline In-Reply-To: <20241106015631.53041587@elisabeth> Message-ID-Hash: N2NG3H53XYYIU3AEIOLXBUS2RACB67OG X-Message-ID-Hash: N2NG3H53XYYIU3AEIOLXBUS2RACB67OG X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: passt-dev@passt.top X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: --ZFGdwF1r9pc/fTmO Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Nov 06, 2024 at 01:56:31AM +0100, Stefano Brivio wrote: > On Tue, 5 Nov 2024 13:32:22 +1100 > David Gibson wrote: >=20 > > In the vhost-user code we have a number of places where we need to loca= te > > a particular header within the guest-supplied IO vector. We need to wo= rk > > out which buffer the header is in, and verify that it's contiguous and > > aligned as we need. At the moment this is open-coded, but introduce a > > helper to make this more straightforward. > >=20 > > We add a new datatype 'struct iov_tail' representing an IO vector from > > which we've logically consumed some number of headers. The IOV_PULL_HE= ADER > > macro consumes a new header from the vector, returning a pointer and > > updating the iov_tail. >=20 > The interfaces look usable and straightforward to me. I find some names > and comments a bit obscure, though. Yeah, I don't love the names either. > First off, I would intuitively say that the "tail" is always at the > end, and if we already consumed something, that's always at the "head". Right.. which is true in a sense. The idea is you'd set one of these up, to cover a whole (say) frame, then pull bits off the front as you need it. So, the iov_tail does represent the "tail", as in the unprocessed bit of the frame at each point... > If we call the whole abstraction "tail", we risk ending up talking about > the tail of the tail, and the head of the tail. Consider this part from > the cover letter: =2E. but, yeah, heads of tails and tails of tails gets confusing. Unless we rewrite in LISP, I guess. > > "iov tail", that is an iov from which you've > > already consumed (in some sense) some data from the beginning.=20 >=20 > ...in other words, that's an IO vector called tail, and we > already consumed some data from its head. >=20 > What about (iov-based) "batch"? I don't think "batch" is really any better, it's just unclear on a different set of axes. Would "iov remainder" be any better? >=20 > > Signed-off-by: David Gibson > > --- > > iov.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > iov.h | 24 +++++++++++++++++ > > 2 files changed, 107 insertions(+) > >=20 > > diff --git a/iov.c b/iov.c > > index 3f9e229..3d384ae 100644 > > --- a/iov.c > > +++ b/iov.c > > @@ -156,3 +156,86 @@ size_t iov_size(const struct iovec *iov, size_t io= v_cnt) > > =20 > > return len; > > } > > + > > +/** > > + * iov_tail_shorten() - Remove any buffers from an IOV tail that are w= holly consumed >=20 > "Remove" is a bit difficult to interpret (does it deallocate? Throw > data away^), I would rather say that we... detach (?) those buffers from > the batch/tail. Yeah, I'm not sure how to express this either. In a sense this operation is a logical no-op: it shouldn't change the results of any future operation, but it might make them slightly faster. > This operation itself raises a question though: if the batch already > carries the information that some buffers were completely consumed, > should it ever be in a state where we want to drop these buffers from > it? >=20 > That is, it sounds like we have some other operation that allows it to > be in an inconsistent state. Sort of, yes, but I think this is the right design choice. It means we can trivially construct one of these things with an arbitrary offset, and we only do the work of stepping through the buffers when we actually have to. We can discard bytes simply by adding to the offset, without having to look at the actual buffers until later. Basically if we want to ensure that the representation is always "minimal", in the sense that the offset lies within the first buffer, then a bunch more operations need to do work to maintain that. Plus, this approach is naturally robust: if we somehow get an iov_tail in non-minimal form, peek/pull will just handle it with no extra logic. > > + * @tail: IO vector tail (modified) > > + * > > + * Return: true if the tail still contains any bytes, otherwise false > > + */ > > +bool iov_tail_shorten(struct iov_tail *tail) > > +{ > > + size_t i; > > + > > + i =3D iov_skip_bytes(tail->iov, tail->cnt, tail->off, &tail->off); > > + tail->iov +=3D i; > > + tail->cnt -=3D i; > > + > > + return !!tail->cnt; > > +} > > + > > +/** > > + * iov_tail_size - Calculate the total size of an IO vector tail > > + * @tail: IO vector tail > > + * > > + * Returns: The total size in bytes. > > + */ > > +/* cppcheck-suppress unusedFunction */ > > +size_t iov_tail_size(struct iov_tail *tail) > > +{ > > + iov_tail_shorten(tail); > > + return iov_size(tail->iov, tail->cnt) - tail->off; > > +} > > + > > +/** > > + * iov_peek_header_() - Get pointer to header from an IOV tail >=20 > I think that this needs to be more generic than "header", because yes, > we're using it for headers, but that word doesn't really help in this > context. Hm. Well, we are using it for headers, and we're also always pulling =66rom the "head" of whatever we have left. > What about "aligned block", or just "block"? Maybe... but it really does have to be from the start of the current tail. >=20 > > + * @tail: IO vector tail to get header from > > + * @len: Length of header to remove in bytes >=20 > to remove, in bytes >=20 > > + * @align: Required alignment of header in bytes >=20 > Judging from this comment alone, it's not clear if 0 or 1 should be > used to get freely aligned blocks. >=20 > > + * > > + * @tail may be modified, but will be semantically equivalent. > > + * > > + * Returns: Pointer to the removed header, NULL if it overruns the IO > > + * vector, is not contiguous or is misaligned. > > + */ > > +void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align) > > +{ > > + char *p; > > + > > + if (!iov_tail_shorten(tail)) > > + return NULL; /* Nothing left */ > > + > > + if (tail->off + len < tail->off) > > + return NULL; /* Overflow */ > > + > > + if (tail->off + len > tail->iov[0].iov_len) > > + return NULL; /* Not contiguous */ >=20 > I'm not sure if this observation is useful in some cases, but this > doesn't necessarily mean that the header/block is not contiguous: if > tail->iov[0].iov_base + tail->iov[0].iov_len =3D=3D tail->iov[1].iov_base, > it actually is. Hm, true. Not sure if it's worth the tests handle that case though. > > + > > + p =3D (char *)tail->iov[0].iov_base + tail->off; > > + if ((uintptr_t)p % align) > > + return NULL; /* not aligned */ > > + > > + return p; > > +} > > +/** > > + * iov_pull_header_() - Remove a header from an IOV tail >=20 > I know that "pulling" is widely used, but it's sometimes ambiguous (I > guess we already had a discussion about that in the past). What about > "remove", or "drop"? "remove" might work. I don't like "drop" because that implies to me it's just gone, rather than returned. > > + * @tail: IO vector tail to remove header from (modified) > > + * @len: Length of header to remove in bytes > > + * @align: Required alignment of header in bytes > > + * > > + * @tail is updated so that it no longer includes the extracted header > > + * > > + * Returns: Pointer to the removed header, NULL if it overruns the IO > > + * vector, is not contiguous or is misaligned. > > + */ > > +/* cppcheck-suppress unusedFunction */ > > +void *iov_pull_header_(struct iov_tail *tail, size_t len, size_t align) > > +{ > > + char *p =3D iov_peek_header_(tail, len, align); > > + > > + if (!p) > > + return NULL; > > + > > + tail->off =3D tail->off + len; >=20 > This could just be +=3D I guess. True. > > + return p; > > +} > > diff --git a/iov.h b/iov.h > > index a9e1722..a2f449c 100644 > > --- a/iov.h > > +++ b/iov.h > > @@ -28,4 +28,28 @@ size_t iov_from_buf(const struct iovec *iov, size_t = iov_cnt, > > size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, > > size_t offset, void *buf, size_t bytes); > > size_t iov_size(const struct iovec *iov, size_t iov_cnt); > > + > > +/** > > + * struct iov_tail - Represents the fail portion of an IO vector >=20 > s/fail/tail/ >=20 > > + * @iov: IO vector > > + * @cnt: Number of entries in @iov > > + * @off: Current offset in @iov > > + */ > > +struct iov_tail { > > + const struct iovec *iov; > > + size_t cnt, off; > > +}; > > + > > +#define IOV_TAIL(iov_, cnt_, off_) \ > > + (struct iov_tail){ .iov =3D (iov_), .cnt =3D (cnt_), .off =3D (off_) } > > + > > +bool iov_tail_shorten(struct iov_tail *tail); > > +size_t iov_tail_size(struct iov_tail *tail); > > +void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align= ); > > +#define IOV_PEEK_HEADER(tail_, ty_) \ > > + ((ty_ *)(iov_peek_header_((tail_), sizeof(ty_), __alignof__(ty_)))) >=20 > I guess 'x' would be as clear as 'ty_' (actually, I'm failing to guess > what it stands for). "type" > > +void *iov_pull_header_(struct iov_tail *tail, size_t len, size_t align= ); > > +#define IOV_PULL_HEADER(tail_, ty_) \ > > + ((ty_ *)(iov_pull_header_((tail_), sizeof(ty_), __alignof__(ty_)))) > > + > > #endif /* IOVEC_H */ >=20 > I would have expected some functions to add data or build those > tails... or we don't need them for some reason? The IOV_TAIL() macro builds one. Adding data doesn't make sense; the idea is this is a view into part of an existing IO vector, which doesn't require copying the struct iovecs of that original vector. --=20 David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson --ZFGdwF1r9pc/fTmO Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmcq1q0ACgkQzQJF27ox 2GeHbA/9Ft0NeFbDea7dM4nxqtyVQ/EikF2vkflyOlw0m5Zvw3qMASiBJjUxGzYf i8tM1ABEz1ou02Vz13nhQ2H91/CSBSpeuRrF5RNfTcisO5lki5Q+OomOv3G8xEMk UfYLW9fUcdkVgWYBcvTiv3IbUUKGDYKYZ8T8AyYt4NMI0gtIA3P+0l7ILgXkb5wU qUuoS7KM9QhNRJa5cHpMRAbYe2oPuzwHIJJNBgDvZaAZ1+6rEY9wXEbwtbYLVDCj KUlXF4bUvU1+QJPyNyqYClmbvKe96YrniNEFezGumk+4HEoAyiQdh8dlnrqMusRj NZ2dyIqQm51fB/sDMt9HmqmMQ5LIT1AT1m6tpfvLagRR6rlMQLdXofpudKf4r1YC MoChurC+rJQ3kNbz4nDaYLjPxlQrXG7zGYAc5Ibpxa/ZmdhDhmiT1Xj2XjZnlN8c pSL5YW3QWuEHDkZgqLlscMfpKkTmcHXjGGCOj1x0JfbYVrqOw9qSdHkn/OUu1dJj ydm7WSjKj8TYxWnmwiCi2Tnw7f2kvk84IOjXqVoSDRdMVrU/Jm32r8mUsgVuWPRU yT11FDVPFeSX9CufGQ65aI/vXArjh3Mwb2uBfGFUY+3L0r7ndcTdGoYNVxEvWUcH iAE/gpQ0oZ2SjWUSjqFHlCyttLR/pAvpq4mB68e3kB6nZJv6z1M= =JCF5 -----END PGP SIGNATURE----- --ZFGdwF1r9pc/fTmO--