blob: 89f3916d85aa5ff34df1159d7d7268e71db0d7df [file] [log] [blame]
// 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;
bool memdevs;
bool endpoint;
} param;
static struct log_ctx pl;
#define BASE_OPTIONS() \
OPT_BOOLEAN(0, "debug", &param.debug, "turn on debug"), \
OPT_BOOLEAN('e', "endpoint", &param.endpoint, \
"target endpoints instead of switch ports")
#define ENABLE_OPTIONS() \
OPT_BOOLEAN('m', "enable-memdevs", &param.memdevs, \
"enable downstream memdev(s)")
#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 const struct option enable_options[] = {
BASE_OPTIONS(),
ENABLE_OPTIONS(),
OPT_END(),
};
static int action_disable(struct cxl_port *port)
{
const char *devname = cxl_port_get_devname(port);
struct cxl_ctx *ctx = cxl_port_get_ctx(port);
struct cxl_memdev *memdev;
int active_memdevs = 0;
if (!cxl_port_is_enabled(port)) {
log_dbg(&pl, "%s already disabled\n", devname);
return 0;
}
if (param.endpoint) {
struct cxl_endpoint *endpoint = cxl_port_to_endpoint(port);
if (cxl_endpoint_get_memdev(endpoint))
active_memdevs++;
}
cxl_memdev_foreach(ctx, memdev) {
if (!cxl_port_get_dport_by_memdev(port, memdev))
continue;
if (cxl_memdev_is_enabled(memdev))
active_memdevs++;
}
if (active_memdevs && !param.force) {
/*
* TODO: actually detect rather than assume active just
* because the memdev is enabled
*/
log_err(&pl,
"%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(&pl,
"See 'cxl list -M -p %s' to see impacted device%s\n",
devname, active_memdevs > 1 ? "s" : "");
return -EBUSY;
}
return cxl_port_disable_invalidate(port);
}
static int action_enable(struct cxl_port *port)
{
struct cxl_ctx *ctx = cxl_port_get_ctx(port);
struct cxl_memdev *memdev;
int rc;
rc = cxl_port_enable(port);
if (rc || !param.memdevs)
return rc;
cxl_memdev_foreach(ctx, memdev)
if (cxl_port_get_dport_by_memdev(port, memdev))
cxl_memdev_enable(memdev);
return 0;
}
static struct cxl_port *find_cxl_port(struct cxl_ctx *ctx, const char *ident)
{
struct cxl_bus *bus;
struct cxl_port *port;
cxl_bus_foreach(ctx, bus)
cxl_port_foreach_all(cxl_bus_get_port(bus), port)
if (util_cxl_port_filter(port, ident, CXL_PF_SINGLE))
return port;
return NULL;
}
static struct cxl_endpoint *find_cxl_endpoint(struct cxl_ctx *ctx,
const char *ident)
{
struct cxl_bus *bus;
struct cxl_port *port;
struct cxl_endpoint *endpoint;
cxl_bus_foreach(ctx, bus)
cxl_port_foreach_all(cxl_bus_get_port(bus), port)
cxl_endpoint_foreach(port, endpoint)
if (util_cxl_endpoint_filter(endpoint, ident))
return endpoint;
return NULL;
}
static int port_action(int argc, const char **argv, struct cxl_ctx *ctx,
int (*action)(struct cxl_port *port),
const struct option *options, const char *usage)
{
int i, rc = 0, count = 0, err = 0;
const char * const u[] = {
usage,
NULL
};
log_init(&pl, "cxl port", "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) {
argc = 1;
break;
}
}
if (param.debug) {
cxl_set_log_priority(ctx, LOG_DEBUG);
pl.log_priority = LOG_DEBUG;
} else
pl.log_priority = LOG_INFO;
rc = 0;
count = 0;
for (i = 0; i < argc; i++) {
struct cxl_port *port;
if (param.endpoint) {
struct cxl_endpoint *endpoint;
endpoint = find_cxl_endpoint(ctx, argv[i]);
if (!endpoint) {
log_notice(&pl, "endpoint: %s not found\n",
argv[i]);
continue;
}
port = cxl_endpoint_get_port(endpoint);
} else {
port = find_cxl_port(ctx, argv[i]);
if (!port) {
log_notice(&pl, "port: %s not found\n",
argv[i]);
continue;
}
}
log_dbg(&pl, "run action on port: %s\n",
cxl_port_get_devname(port));
rc = action(port);
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_port(int argc, const char **argv, struct cxl_ctx *ctx)
{
int count = port_action(
argc, argv, ctx, action_disable, disable_options,
"cxl disable-port <port0> [<port1>..<portN>] [<options>]");
log_info(&pl, "disabled %d port%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx)
{
int count = port_action(
argc, argv, ctx, action_enable, enable_options,
"cxl enable-port <port0> [<port1>..<portN>] [<options>]");
log_info(&pl, "enabled %d port%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}