[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [PATCH 1/4] Refactor request handling



On Tue, Mar 14, 2023 at 10:44:54AM -0500, Eric Blake wrote:
> On Sat, Mar 11, 2023 at 03:07:02PM +0200, w@uter.be wrote:
> > From: Wouter Verhelst <w@uter.be>
> > 
> > Currently, the state of a request is stored on the stack, with the reply
> > being built in a buffer which is sent out at the very end of the
> > 'handle_normal_read' function.
> > 
> > This makes implementing structured replies complicated, as for those we
> > may want to sometimes send a reply chunk before that point.
> > 
> > Thus, refactor request handling such that we no longer depend on the
> > stack for sending out things.
> > 
> > Signed-off-by: Wouter Verhelst <w@uter.be>
> > ---
> >  nbd-server.c | 144 +++++++++++++++++++++++++++++++++++----------------
> >  nbdsrv.h     |   8 +++
> >  2 files changed, 106 insertions(+), 46 deletions(-)
> 
> >  /**
> >   * Read an amount of bytes at a given offset from the right file. This
> >   * abstracts the read-side of the multiple files option.
> > @@ -1310,15 +1354,20 @@ ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
> >   * Call rawexpread repeatedly until all data has been read.
> >   * @return 0 on success, nonzero on failure
> >   **/
> > -int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
> > +int rawexpread_fully(READ_CTX *ctx, CLIENT *client) {
> >  	ssize_t ret=0;
> >  
> > -	while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
> > -		a += ret;
> > -		buf += ret;
> > -		len -= ret;
> > +	char *buf;
> > +
> > +	while(ctx->current_len > 0) {
> > +		buf = find_read_buf(ctx);
> > +		if((ret = rawexpread((off_t)ctx->req->from + (off_t)ctx->current_offset, buf, ctx->current_len, client)) < 0) {
> > +			return ret;
> > +		}
> > +		ctx->current_offset += ret;
> > +		ctx->current_len -= ret;
> >  	}
> > -	return (ret < 0 || len != 0);
> 
> The old code looped until an error was detected or a short read occurs
> (ret == 0, len != 0).
> 
> > +	return (ret < 0 || ctx->current_len != 0);
> 
> But the new code loops only until ctx->current_len is 0 (with early
> exit on read error), making the right side of the || dead code, and
> making the loop itself busy-loop on a persistently short read.
> Quickest fix would be inserting
> 
>     if (ret == 0) break;
> 
> after the if((ret = ...)) condition, and leaving the || in the return
> untouched.

Thanks, good catch.

I've changed it to 

if((ret = rawexpread(...)) <= 0) {
	break;
}

which seems like an even better idea (and is closer to the original
behavior).

> ....
> > +++ b/nbdsrv.h
> > @@ -89,6 +89,14 @@ typedef struct {
> >  	off_t startoff;   /**< starting offset of this file */
> >  } FILE_INFO;
> >  
> > +typedef struct {
> > +	struct nbd_request *req;
> > +	char *buf;
> > +	size_t buflen;
> > +	size_t current_offset;
> 
> Should this be an off_t to begin with, or do we not care about 32-bit platforms?

current_offset holds how much of the request we have already handled,
not a file offset. In the case of simple replies (or structured replies
with DF set), the reply is built into an allocated buffer, and in that
case current_offset is used in pointer arithmetic (which implies it
needs to be a size_t, or be at least cast to that).

It's true that it isn't used in pointer arithmetics in the case of
structured replies, since there we allocate a fresh buffer every time we
get into find_read_buf. However, I still want to think of file_read_buf
as a function returning a, possibly offset, pointer into a buffer even
in that case, which also implies it should be a size_t rather than an
off_t.

Of course, in either case current_offset is *added* to a file offset in
order to find the point where we should start reading, but that is only
a secondary usage, not a primary one.

But perhaps I'm missing something?

-- 
     w@uter.{be,co.za}
wouter@{grep.be,fosdem.org,debian.org}

I will have a Tin-Actinium-Potassium mixture, thanks.


Reply to: