From 675382f12b67e8b7b2f35bd1f8dfd96b8d8e4aae Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Tue, 26 May 2026 12:37:39 -0700 Subject: [PATCH] p9fs: Refactor buffer allocations to avoid zeroing large payloads Allocating large buffers with M_ZERO adds unnecessary overhead since the data is immediately overwritten. This change embeds the tc and rc p9_buffer structs directly into p9_req_t so we only zero the small metadata headers. The actual data payload is allocated with M_NOWAIT. Embedding the metadata headers by value also allows the p9fs_buf_zone UMA items to be sized exactly to P9FS_MTU, ensuring they are nicely aligned. This also adds proper error handling to p9_get_request() to handle UMA allocation failures. Reviewed by: markj MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D56495 --- sys/dev/virtio/p9fs/virtio_p9fs.c | 10 ++-- sys/fs/p9fs/p9_client.c | 95 ++++++++++++++----------------- sys/fs/p9fs/p9_client.h | 4 +- 3 files changed, 49 insertions(+), 60 deletions(-) diff --git a/sys/dev/virtio/p9fs/virtio_p9fs.c b/sys/dev/virtio/p9fs/virtio_p9fs.c index 19a32fea458e..c347458b4f8e 100644 --- a/sys/dev/virtio/p9fs/virtio_p9fs.c +++ b/sys/dev/virtio/p9fs/virtio_p9fs.c @@ -112,7 +112,7 @@ SYSCTL_UINT(_vfs_9p, OID_AUTO, ackmaxidle, CTLFLAG_RW, &vt9p_ackmaxidle, 0, static int vt9p_req_wait(struct vt9p_softc *chan, struct p9_req_t *req) { - KASSERT(req->tc->tag != req->rc->tag, + KASSERT(req->tc.tag != req->rc.tag, ("%s: request %p already completed", __func__, req)); if (msleep(req, VT9P_MTX(chan), 0, "chan lock", vt9p_ackmaxidle * hz)) { @@ -124,7 +124,7 @@ vt9p_req_wait(struct vt9p_softc *chan, struct p9_req_t *req) "for an ack from host\n", vt9p_ackmaxidle); return (EIO); } - KASSERT(req->tc->tag == req->rc->tag, + KASSERT(req->tc.tag == req->rc.tag, ("%s spurious event on request %p", __func__, req)); return (0); } @@ -157,7 +157,7 @@ vt9p_request(void *handle, struct p9_req_t *req) req_retry: sglist_reset(sg); /* Handle out VirtIO ring buffers */ - error = sglist_append(sg, req->tc->sdata, req->tc->size); + error = sglist_append(sg, req->tc.sdata, req->tc.size); if (error != 0) { P9_DEBUG(ERROR, "%s: sglist append failed\n", __func__); VT9P_UNLOCK(chan); @@ -165,7 +165,7 @@ req_retry: } readable = sg->sg_nseg; - error = sglist_append(sg, req->rc->sdata, req->rc->capacity); + error = sglist_append(sg, req->rc.sdata, req->rc.capacity); if (error != 0) { P9_DEBUG(ERROR, "%s: sglist append failed\n", __func__); VT9P_UNLOCK(chan); @@ -226,7 +226,7 @@ vt9p_intr_complete(void *xsc) VT9P_LOCK(chan); again: while ((curreq = virtqueue_dequeue(vq, NULL)) != NULL) { - curreq->rc->tag = curreq->tc->tag; + curreq->rc.tag = curreq->tc.tag; wakeup_one(curreq); } if (virtqueue_enable_intr(vq) != 0) { diff --git a/sys/fs/p9fs/p9_client.c b/sys/fs/p9fs/p9_client.c index 547de98c4c03..27a6c1eb366d 100644 --- a/sys/fs/p9fs/p9_client.c +++ b/sys/fs/p9fs/p9_client.c @@ -104,43 +104,33 @@ p9_parse_opts(struct mount *mp, struct p9_client *clnt) } /* Allocate buffer for sending request and getting responses */ -static struct p9_buffer * -p9_buffer_alloc(int alloc_msize) +static void +p9_buffer_alloc(struct p9_buffer *fc, int alloc_msize) { - struct p9_buffer *fc; - - fc = uma_zalloc(p9fs_buf_zone, M_WAITOK | M_ZERO); + bzero(fc, sizeof(*fc)); fc->capacity = alloc_msize; - fc->offset = 0; - fc->size = 0; - fc->sdata = (char *)fc + sizeof(struct p9_buffer); - - return (fc); + fc->sdata = uma_zalloc(p9fs_buf_zone, M_WAITOK); } /* Free memory used by request and response buffers */ static void -p9_buffer_free(struct p9_buffer **buf) +p9_buffer_free(struct p9_buffer *buf) { - - /* Free the sdata buffers first, then the whole structure*/ - uma_zfree(p9fs_buf_zone, *buf); - *buf = NULL; + uma_zfree(p9fs_buf_zone, buf->sdata); + buf->sdata = NULL; } /* Free the request */ static void p9_free_req(struct p9_client *clnt, struct p9_req_t *req) { + if (req == NULL) + return; - if (req->tc != NULL) { - if (req->tc->tag != P9_NOTAG) - p9_tag_destroy(clnt, req->tc->tag); - p9_buffer_free(&req->tc); - } - - if (req->rc != NULL) - p9_buffer_free(&req->rc); + if (req->tc.tag != P9_NOTAG) + p9_tag_destroy(clnt, req->tc.tag); + p9_buffer_free(&req->tc); + p9_buffer_free(&req->rc); uma_zfree(p9fs_req_zone, req); } @@ -156,17 +146,17 @@ p9_get_request(struct p9_client *clnt, int *error) alloc_msize = P9FS_MTU; req = uma_zalloc(p9fs_req_zone, M_WAITOK | M_ZERO); - req->tc = p9_buffer_alloc(alloc_msize); - req->rc = p9_buffer_alloc(alloc_msize); + p9_buffer_alloc(&req->tc, alloc_msize); + p9_buffer_alloc(&req->rc, alloc_msize); tag = p9_tag_create(clnt); if (tag == P9_NOTAG) { *error = EAGAIN; - req->tc->tag = P9_NOTAG; + req->tc.tag = P9_NOTAG; p9_free_req(clnt, req); return (NULL); } - req->tc->tag = tag; + req->tc.tag = tag; return (req); } @@ -208,7 +198,7 @@ p9_client_check_return(struct p9_client *c, struct p9_req_t *req) char *ename; /* Check what we have in the receive bufer .*/ - error = p9_parse_receive(req->rc, c); + error = p9_parse_receive(&req->rc, c); if (error != 0) goto out; @@ -216,17 +206,17 @@ p9_client_check_return(struct p9_client *c, struct p9_req_t *req) * No error, We are done with the preprocessing. Return to the caller * and process the actual data. */ - if (req->rc->id != P9PROTO_RERROR && req->rc->id != P9PROTO_RLERROR) + if (req->rc.id != P9PROTO_RERROR && req->rc.id != P9PROTO_RLERROR) return (0); /* * Interpreting the error is done in different ways for Linux and * Unix version. Make sure you interpret it right. */ - if (req->rc->id == P9PROTO_RERROR) { - error = p9_buf_readf(req->rc, c->proto_version, "s?d", &ename, &ecode); - } else if (req->rc->id == P9PROTO_RLERROR) { - error = p9_buf_readf(req->rc, c->proto_version, "d", &ecode); + if (req->rc.id == P9PROTO_RERROR) { + error = p9_buf_readf(&req->rc, c->proto_version, "s?d", &ename, &ecode); + } else if (req->rc.id == P9PROTO_RLERROR) { + error = p9_buf_readf(&req->rc, c->proto_version, "d", &ecode); } else { goto out; } @@ -241,15 +231,15 @@ p9_client_check_return(struct p9_client *c, struct p9_req_t *req) * not present can hit this and return. Hence it is made a debug print. */ if (error != 0) { - if (req->rc->id == P9PROTO_RERROR) { + if (req->rc.id == P9PROTO_RERROR) { P9_DEBUG(PROTO, "RERROR error %d ename %s\n", error, ename); - } else if (req->rc->id == P9PROTO_RLERROR) { + } else if (req->rc.id == P9PROTO_RLERROR) { P9_DEBUG(PROTO, "RLERROR error %d\n", error); } } - if (req->rc->id == P9PROTO_RERROR) { + if (req->rc.id == P9PROTO_RERROR) { free(ename, M_TEMP); } return (error); @@ -308,21 +298,21 @@ p9_client_prepare_req(struct p9_client *c, int8_t type, } /* Marshall the data according to QEMU standards */ - *error = p9_buf_prepare(req->tc, type); + *error = p9_buf_prepare(&req->tc, type); if (*error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_prepare failed: %d\n", __func__, *error); goto out; } - *error = p9_buf_vwritef(req->tc, c->proto_version, fmt, ap); + *error = p9_buf_vwritef(&req->tc, c->proto_version, fmt, ap); if (*error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_vwrite failed: %d\n", __func__, *error); goto out; } - *error = p9_buf_finalize(c, req->tc); + *error = p9_buf_finalize(c, &req->tc); if (*error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_finalize failed: %d \n", __func__, *error); @@ -474,7 +464,7 @@ p9_client_version(struct p9_client *c) if (error != 0) return (error); - error = p9_buf_readf(req->rc, c->proto_version, "ds", &msize, &version); + error = p9_buf_readf(&req->rc, c->proto_version, "ds", &msize, &version); if (error != 0) { P9_DEBUG(ERROR, "%s: version error: %d\n", __func__, error); goto out; @@ -519,8 +509,7 @@ p9_init_zones(void) /* Create the buffer zone */ p9fs_buf_zone = uma_zcreate("p9fs buf zone", - sizeof(struct p9_buffer) + P9FS_MTU, NULL, NULL, - NULL, NULL, UMA_ALIGN_PTR, 0); + P9FS_MTU, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } void @@ -623,7 +612,7 @@ p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, if (*error != 0) goto out; - *error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid); + *error = p9_buf_readf(&req->rc, clnt->proto_version, "Q", &qid); if (*error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_readf failed: %d \n", __func__, *error); @@ -777,7 +766,7 @@ p9_client_walk(struct p9_fid *oldfid, uint16_t nwnames, char **wnames, return (NULL); } - *error = p9_buf_readf(req->rc, clnt->proto_version, "R", &nwqids, + *error = p9_buf_readf(&req->rc, clnt->proto_version, "R", &nwqids, &wqids); if (*error != 0) goto out; @@ -842,7 +831,7 @@ p9_client_open(struct p9_fid *fid, int mode) if (error != 0) return (error); - error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &fid->qid, + error = p9_buf_readf(&req->rc, clnt->proto_version, "Qd", &fid->qid, &mtu); if (error != 0) goto out; @@ -892,7 +881,7 @@ p9_client_readdir(struct p9_fid *fid, char *data, uint64_t offset, return (-error); } - error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count, + error = p9_buf_readf(&req->rc, clnt->proto_version, "D", &count, &dataptr); if (error != 0) { P9_DEBUG(ERROR, "%s: p0_buf_readf failed: %d\n", @@ -945,7 +934,7 @@ p9_client_read(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data) return (-error); } - error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count, + error = p9_buf_readf(&req->rc, clnt->proto_version, "D", &count, &dataptr); if (error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_readf failed: %d\n", @@ -1017,7 +1006,7 @@ p9_client_write(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data) return (-error); } - error = p9_buf_readf(req->rc, clnt->proto_version, "d", &ret); + error = p9_buf_readf(&req->rc, clnt->proto_version, "d", &ret); if (error != 0) { P9_DEBUG(ERROR, "%s: p9_buf_readf error: %d\n", __func__, error); @@ -1069,7 +1058,7 @@ p9_client_file_create(struct p9_fid *fid, char *name, uint32_t perm, int mode, if (error != 0) return (error); - error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &qid, &mtu); + error = p9_buf_readf(&req->rc, clnt->proto_version, "Qd", &qid, &mtu); if (error != 0) goto out; @@ -1101,7 +1090,7 @@ p9_client_statfs(struct p9_fid *fid, struct p9_statfs *stat) return (error); } - error = p9_buf_readf(req->rc, clnt->proto_version, "ddqqqqqqd", + error = p9_buf_readf(&req->rc, clnt->proto_version, "ddqqqqqqd", &stat->type, &stat->bsize, &stat->blocks, &stat->bfree, &stat->bavail, &stat->files, &stat->ffree, &stat->fsid, &stat->namelen); @@ -1173,7 +1162,7 @@ p9_create_symlink(struct p9_fid *fid, char *name, char *symtgt, gid_t gid) if (error != 0) return (error); - error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid); + error = p9_buf_readf(&req->rc, clnt->proto_version, "Q", &qid); if (error != 0) { P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, error); return (error); @@ -1226,7 +1215,7 @@ p9_readlink(struct p9_fid *fid, char **target) if (error != 0) return (error); - error = p9_buf_readf(req->rc, clnt->proto_version, "s", target); + error = p9_buf_readf(&req->rc, clnt->proto_version, "s", target); if (error != 0) { P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, error); return (error); @@ -1260,7 +1249,7 @@ p9_client_getattr(struct p9_fid *fid, struct p9_stat_dotl *stat_dotl, goto error; } - err = p9_buf_readf(req->rc, clnt->proto_version, "A", stat_dotl); + err = p9_buf_readf(&req->rc, clnt->proto_version, "A", stat_dotl); if (err != 0) { P9_DEBUG(ERROR, "%s: buf_readf failed %d\n", __func__, err); goto error; diff --git a/sys/fs/p9fs/p9_client.h b/sys/fs/p9fs/p9_client.h index 4eb82c0232f4..5db46d97c704 100644 --- a/sys/fs/p9fs/p9_client.h +++ b/sys/fs/p9fs/p9_client.h @@ -54,8 +54,8 @@ enum p9_proto_versions { /* P9 Request exchanged between Host and Guest */ struct p9_req_t { - struct p9_buffer *tc; /* request buffer */ - struct p9_buffer *rc; /* response buffer */ + struct p9_buffer tc; /* request buffer */ + struct p9_buffer rc; /* response buffer */ }; /* 9P transport status */