git: 555a861d6826 - main - device_get_path(): take sbuf directly

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Wed, 19 Oct 2022 16:40:03 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=555a861d6826eca8710e361ee4d0ef29b28f39d2

commit 555a861d6826eca8710e361ee4d0ef29b28f39d2
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2022-10-08 00:08:14 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-10-19 16:39:40 +0000

    device_get_path(): take sbuf directly
    
    This allows to fix a bug where sbuf allocation done in the context of
    dev_wired_cache_match() must use non-sleepable allocations.
    
    Suggested by:   jhb
    Reviewed by:    jhb, takawata
    Discussed with: imp
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D36899
---
 sys/kern/subr_bus.c | 73 +++++++++++++++++++++++++----------------------------
 1 file changed, 35 insertions(+), 38 deletions(-)

diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 74bc47174d8d..5c165419af2d 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -5305,37 +5305,24 @@ device_do_deferred_actions(void)
 }
 
 static int
-device_get_path(device_t dev, const char *locator, char **rvp)
+device_get_path(device_t dev, const char *locator, struct sbuf *sb)
 {
-	struct sbuf *sb;
-	char *s;
 	device_t parent;
-	ssize_t len;
 	int error;
 
+	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
 	parent = device_get_parent(dev);
 	if (parent == NULL) {
-		*rvp = strdup_flags("/", M_BUS, M_NOWAIT);
-		return (*rvp == NULL ? ENOMEM : 0);
-	}
-	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
-	error = BUS_GET_DEVICE_PATH(parent, dev, locator, sb);
-	sbuf_finish(sb);	/* Note: errors checked with sbuf_len() below */
-	if (error == 0) {
-		len = sbuf_len(sb);
-		if (len <= 1) {
-			error = EIO;
-		} else {
-			s = malloc(len, M_BUS, M_NOWAIT);
-			if (s == NULL) {
-				error = ENOMEM;
-			} else {
-				memcpy(s, sbuf_data(sb), len);
-				*rvp = s;
-			}
+		error = sbuf_printf(sb, "/");
+	} else {
+		error = BUS_GET_DEVICE_PATH(parent, dev, locator, sb);
+		if (error == 0) {
+			error = sbuf_error(sb);
+			if (error == 0 && sbuf_len(sb) <= 1)
+				error = EIO;
 		}
 	}
-	sbuf_delete(sb);
+	sbuf_finish(sb);
 	return (error);
 }
 
@@ -5599,25 +5586,28 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
 		    req->dr_flags);
 		break;
 	case DEV_GET_PATH: {
+		struct sbuf *sb;
 		char locator[64];
-		char *path;
 		ssize_t len;
 
 		error = copyinstr(req->dr_buffer.buffer, locator,
 		    sizeof(locator), NULL);
 		if (error != 0)
 			break;
-		error = device_get_path(dev, locator, &path);
-		if (error != 0)
-			break;
-		len = strlen(path) + 1;
-		if (req->dr_buffer.length < len) {
-			error = ENAMETOOLONG;
-		} else {
-			error = copyout(path, req->dr_buffer.buffer, len);
+		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND |
+		    SBUF_INCLUDENUL /* | SBUF_WAITOK */);
+		error = device_get_path(dev, locator, sb);
+		if (error == 0) {
+			len = sbuf_len(sb);
+			if (req->dr_buffer.length < len) {
+				error = ENAMETOOLONG;
+			} else {
+				error = copyout(sbuf_data(sb),
+				    req->dr_buffer.buffer, len);
+			}
+			req->dr_buffer.length = len;
 		}
-		req->dr_buffer.length = len;
-		free(path, M_BUS);
+		sbuf_delete(sb);
 		break;
 	}
 	}
@@ -5712,8 +5702,8 @@ bool
 dev_wired_cache_match(device_location_cache_t *dcp, device_t dev,
     const char *at)
 {
+	struct sbuf *sb;
 	const char *cp;
-	char *path;
 	char locator[32];
 	int error, len;
 	struct device_location_node *res;
@@ -5732,9 +5722,16 @@ dev_wired_cache_match(device_location_cache_t *dcp, device_t dev,
 	/* maybe cache this inside device_t and look that up, but not yet */
 	res = dev_wired_cache_lookup(dcp, locator);
 	if (res == NULL) {
-		error = device_get_path(dev, locator, &path);
-		if (error == 0)
-			res = dev_wired_cache_add(dcp, locator, path);
+		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND |
+		    SBUF_INCLUDENUL | SBUF_NOWAIT);
+		if (sb != NULL) {
+			error = device_get_path(dev, locator, sb);
+			if (error == 0) {
+				res = dev_wired_cache_add(dcp, locator,
+				    sbuf_data(sb));
+			}
+			sbuf_delete(sb);
+		}
 	}
 	if (error != 0 || res == NULL || res->dln_path == NULL)
 		return (false);