All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dan Williams <dan.j.williams@intel.com>
To: vishal.l.verma@intel.com
Cc: linux-cxl@vger.kernel.org, nvdimm@lists.linux.dev
Subject: [ndctl PATCH 05/10] cxl/bus: Add bus disable support
Date: Thu, 28 Apr 2022 15:10:27 -0700	[thread overview]
Message-ID: <165118382738.1676208.16851880881648171660.stgit@dwillia2-desk3.amr.corp.intel.com> (raw)
In-Reply-To: <165118380037.1676208.7644295506592461996.stgit@dwillia2-desk3.amr.corp.intel.com>

Route requests to disable the root back to unbinding the platform firmware
device, ACPI0017 for ACPI.CXL platforms.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 Documentation/cxl/cxl-disable-bus.txt |   37 ++++++++
 Documentation/cxl/lib/libcxl.txt      |   12 ++
 Documentation/cxl/meson.build         |    1 
 cxl/builtin.h                         |    1 
 cxl/bus.c                             |  159 +++++++++++++++++++++++++++++++++
 cxl/cxl.c                             |    1 
 cxl/filter.c                          |    3 -
 cxl/filter.h                          |    1 
 cxl/lib/libcxl.c                      |   15 +++
 cxl/lib/libcxl.sym                    |    1 
 cxl/libcxl.h                          |    1 
 cxl/meson.build                       |    1 
 12 files changed, 231 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/cxl/cxl-disable-bus.txt
 create mode 100644 cxl/bus.c

diff --git a/Documentation/cxl/cxl-disable-bus.txt b/Documentation/cxl/cxl-disable-bus.txt
new file mode 100644
index 000000000000..65f695cd06c8
--- /dev/null
+++ b/Documentation/cxl/cxl-disable-bus.txt
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+
+cxl-disable-bus(1)
+===================
+
+NAME
+----
+cxl-disable-bus - Shutdown an entire tree of CXL devices
+
+SYNOPSIS
+--------
+[verse]
+'cxl disable-bus' <root0> [<root1>..<rootN>] [<options>]
+
+For test and debug scenarios, disable a CXL bus and any associated
+memory devices from CXL.mem operations.
+
+OPTIONS
+-------
+-f::
+--force::
+	DANGEROUS: Override the safety measure that blocks attempts to disable a
+	bus if the tool determines a descendent memdev is in active usage.
+	Recall that CXL memory ranges might have been established by platform
+	firmware and disabling an active device is akin to force removing memory
+	from a running system.
+
+--debug::
+	If the cxl tool was built with debug disabled, turn on debug
+	messages.
+
+
+include::../copyright.txt[]
+
+SEE ALSO
+--------
+linkcxl:cxl-disable-port[1]
diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt
index 7b223cbcac3f..f8f0e668ab59 100644
--- a/Documentation/cxl/lib/libcxl.txt
+++ b/Documentation/cxl/lib/libcxl.txt
@@ -216,6 +216,18 @@ discovery order. The possible provider names are 'ACPI.CXL' and
 the kernel device names that are subject to change based on discovery
 order.
 
+=== BUS: Control
+----
+int cxl_bus_disable_invalidate(struct cxl_bus *bus);
+----
+
+An entire CXL topology can be torn down with this API. Like other
+_invalidate APIs callers must assume that all library objects have been
+freed. This one goes one step further and also frees the @bus argument.
+This may crash the system and is only useful in kernel driver
+development scenarios.
+
+
 PORTS
 -----
 CXL ports track the PCIe hierarchy between a platform firmware CXL root
diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build
index e927644a3826..974a5a41d169 100644
--- a/Documentation/cxl/meson.build
+++ b/Documentation/cxl/meson.build
@@ -34,6 +34,7 @@ cxl_manpages = [
   'cxl-disable-memdev.txt',
   'cxl-enable-port.txt',
   'cxl-disable-port.txt',
+  'cxl-disable-bus.txt',
   'cxl-set-partition.txt',
 ]
 
diff --git a/cxl/builtin.h b/cxl/builtin.h
index 7bbad98f67ac..a437bc314a30 100644
--- a/cxl/builtin.h
+++ b/cxl/builtin.h
@@ -15,4 +15,5 @@ int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx);
 int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx);
 int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx);
 int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx);
+int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx);
 #endif /* _CXL_BUILTIN_H_ */
diff --git a/cxl/bus.c b/cxl/bus.c
new file mode 100644
index 000000000000..33212951a404
--- /dev/null
+++ b/cxl/bus.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <util/log.h>
+#include <cxl/libcxl.h>
+#include <util/parse-options.h>
+#include <ccan/minmax/minmax.h>
+#include <ccan/array_size/array_size.h>
+
+#include "filter.h"
+
+static struct parameters {
+	bool debug;
+	bool force;
+} param;
+
+static struct log_ctx bl;
+
+#define BASE_OPTIONS()                                                 \
+OPT_BOOLEAN(0, "debug", &param.debug, "turn on debug")
+
+#define DISABLE_OPTIONS()                                              \
+OPT_BOOLEAN('f', "force", &param.force,                                \
+	    "DANGEROUS: override active memdev safety checks")
+
+static const struct option disable_options[] = {
+	BASE_OPTIONS(),
+	DISABLE_OPTIONS(),
+	OPT_END(),
+};
+
+static int action_disable(struct cxl_bus *bus)
+{
+	const char *devname = cxl_bus_get_devname(bus);
+	struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
+	struct cxl_memdev *memdev;
+	int active_memdevs = 0;
+
+	cxl_memdev_foreach(ctx, memdev)
+		if (bus == cxl_memdev_get_bus(memdev))
+			active_memdevs++;
+
+	if (active_memdevs && !param.force) {
+		/*
+		 * TODO: actually detect rather than assume active just
+		 * because the memdev is enabled
+		 */
+		log_err(&bl,
+			"%s hosts %d memdev%s which %s part of an active region\n",
+			devname, active_memdevs, active_memdevs > 1 ? "s" : "",
+			active_memdevs > 1 ? "are" : "is");
+		log_err(&bl,
+			"See 'cxl list -M -b %s' to see impacted device%s\n",
+			devname, active_memdevs > 1 ? "s" : "");
+		return -EBUSY;
+	}
+
+	return cxl_bus_disable_invalidate(bus);
+}
+
+static struct cxl_bus *find_cxl_bus(struct cxl_ctx *ctx, const char *ident)
+{
+	struct cxl_bus *bus;
+
+	cxl_bus_foreach(ctx, bus)
+		if (util_cxl_bus_filter(bus, ident))
+			return bus;
+	return NULL;
+}
+
+static int bus_action(int argc, const char **argv, struct cxl_ctx *ctx,
+		      int (*action)(struct cxl_bus *bus),
+		      const struct option *options, const char *usage)
+{
+	int i, rc = 0, count = 0, err = 0;
+	const char * const u[] = {
+		usage,
+		NULL
+	};
+	unsigned long id;
+
+	log_init(&bl, "cxl bus", "CXL_PORT_LOG");
+	argc = parse_options(argc, argv, options, u, 0);
+
+	if (argc == 0)
+		usage_with_options(u, options);
+	for (i = 0; i < argc; i++) {
+		if (strcmp(argv[i], "all") == 0) {
+			argv[0] = "all";
+			argc = 1;
+			break;
+		}
+
+		if (sscanf(argv[i], "root%lu", &id) == 1)
+			continue;
+		if (sscanf(argv[i], "%lu", &id) == 1)
+			continue;
+
+		log_err(&bl, "'%s' is not a valid bus identifer\n", argv[i]);
+		err++;
+	}
+
+	if (err == argc) {
+		usage_with_options(u, options);
+		return -EINVAL;
+	}
+
+	if (param.debug) {
+		cxl_set_log_priority(ctx, LOG_DEBUG);
+		bl.log_priority = LOG_DEBUG;
+	} else
+		bl.log_priority = LOG_INFO;
+
+	rc = 0;
+	err = 0;
+	count = 0;
+
+	for (i = 0; i < argc; i++) {
+		struct cxl_bus *bus;
+
+		bus = find_cxl_bus(ctx, argv[i]);
+		if (!bus) {
+			log_dbg(&bl, "bus: %s not found\n", argv[i]);
+			continue;
+		}
+
+		log_dbg(&bl, "run action on bus: %s\n",
+			cxl_bus_get_devname(bus));
+		rc = action(bus);
+		if (rc == 0)
+			count++;
+		else if (rc && !err)
+			err = rc;
+	}
+	rc = err;
+
+	/*
+	 * count if some actions succeeded, 0 if none were attempted,
+	 * negative error code otherwise.
+	 */
+	if (count > 0)
+		return count;
+	return rc;
+}
+
+ int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx)
+ {
+	 int count = bus_action(
+		 argc, argv, ctx, action_disable, disable_options,
+		 "cxl disable-bus <bus0> [<bus1>..<busN>] [<options>]");
+
+	 log_info(&bl, "disabled %d bus%s\n", count >= 0 ? count : 0,
+		  count > 1 ? "s" : "");
+	 return count >= 0 ? 0 : EXIT_FAILURE;
+ }
diff --git a/cxl/cxl.c b/cxl/cxl.c
index ab4bbeccaa76..aa4ce61b7c87 100644
--- a/cxl/cxl.c
+++ b/cxl/cxl.c
@@ -69,6 +69,7 @@ static struct cmd_struct commands[] = {
 	{ "disable-port", .c_fn = cmd_disable_port },
 	{ "enable-port", .c_fn = cmd_enable_port },
 	{ "set-partition", .c_fn = cmd_set_partition },
+	{ "disable-bus", .c_fn = cmd_disable_bus },
 };
 
 int main(int argc, const char **argv)
diff --git a/cxl/filter.c b/cxl/filter.c
index b3396426dda8..c6ab9eb58124 100644
--- a/cxl/filter.c
+++ b/cxl/filter.c
@@ -176,8 +176,7 @@ util_cxl_decoder_filter_by_port(struct cxl_decoder *decoder, const char *ident,
 	return NULL;
 }
 
-static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus,
-					   const char *__ident)
+struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident)
 {
 	char *ident, *save;
 	const char *arg;
diff --git a/cxl/filter.h b/cxl/filter.h
index 697b7779c08e..955794366d5c 100644
--- a/cxl/filter.h
+++ b/cxl/filter.h
@@ -41,6 +41,7 @@ enum cxl_port_filter_mode {
 
 struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident,
 				      enum cxl_port_filter_mode mode);
+struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident);
 struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint,
 					      const char *__ident);
 struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target,
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
index 59e164464987..0e8dd20e3c47 100644
--- a/cxl/lib/libcxl.c
+++ b/cxl/lib/libcxl.c
@@ -556,6 +556,21 @@ static void bus_invalidate(struct cxl_bus *bus)
 	cxl_flush(ctx);
 }
 
+CXL_EXPORT int cxl_bus_disable_invalidate(struct cxl_bus *bus)
+{
+	struct cxl_ctx *ctx = cxl_bus_get_ctx(bus);
+	struct cxl_port *port = cxl_bus_get_port(bus);
+	int rc;
+
+	rc = util_unbind(port->uport, ctx);
+	if (rc)
+		return rc;
+
+	free_bus(bus, &ctx->buses);
+	cxl_flush(ctx);
+	return 0;
+}
+
 CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev)
 {
 	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
index aab1112a91d8..dffcb60b8dd0 100644
--- a/cxl/lib/libcxl.sym
+++ b/cxl/lib/libcxl.sym
@@ -86,6 +86,7 @@ global:
 	cxl_bus_get_id;
 	cxl_bus_get_port;
 	cxl_bus_get_ctx;
+	cxl_bus_disable_invalidate;
 	cxl_port_get_first;
 	cxl_port_get_next;
 	cxl_port_get_devname;
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
index 0063d31ab398..0007f4d9bcee 100644
--- a/cxl/libcxl.h
+++ b/cxl/libcxl.h
@@ -73,6 +73,7 @@ const char *cxl_bus_get_devname(struct cxl_bus *bus);
 int cxl_bus_get_id(struct cxl_bus *bus);
 struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus);
 struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus);
+int cxl_bus_disable_invalidate(struct cxl_bus *bus);
 
 #define cxl_bus_foreach(ctx, bus)                                              \
 	for (bus = cxl_bus_get_first(ctx); bus != NULL;                        \
diff --git a/cxl/meson.build b/cxl/meson.build
index 671c8e1626ef..d63dcb12eec2 100644
--- a/cxl/meson.build
+++ b/cxl/meson.build
@@ -2,6 +2,7 @@ cxl_src = [
   'cxl.c',
   'list.c',
   'port.c',
+  'bus.c',
   'memdev.c',
   'json.c',
   'filter.c',


  parent reply	other threads:[~2022-04-28 22:10 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-28 22:10 [ndctl PATCH 00/10] CXL topology unit test Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 01/10] build: Move utility helpers to libutil.a Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 02/10] util: Use SZ_ size macros in display size Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 03/10] util: Pretty print terabytes Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 04/10] cxl/port: Fix disable-port man page Dan Williams
2022-04-28 22:10 ` Dan Williams [this message]
2022-04-28 22:10 ` [ndctl PATCH 06/10] cxl/list: Auto-enable 'single' mode for port listings Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 07/10] cxl/memdev: Fix bus_invalidate() crash Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 08/10] cxl/list: Add support for filtering by host identifiers Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 09/10] cxl/port: Relax port identifier validation Dan Williams
2022-04-28 22:10 ` [ndctl PATCH 10/10] cxl/test: Add topology enumeration and hotplug test Dan Williams

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=165118382738.1676208.16851880881648171660.stgit@dwillia2-desk3.amr.corp.intel.com \
    --to=dan.j.williams@intel.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=nvdimm@lists.linux.dev \
    --cc=vishal.l.verma@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.