[v3,1/2] raw/cnxk_gpio: support multi-process mode

Message ID 20231016100422.1476232-1-tduszynski@marvell.com (mailing list archive)
State Accepted, archived
Delegated to: Jerin Jacob
Headers
Series [v3,1/2] raw/cnxk_gpio: support multi-process mode |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Tomasz Duszynski Oct. 16, 2023, 10:04 a.m. UTC
  GPIO PMD uses a mixture of standard sysfs attributes and custom
ioctl()s to control behaviour of respective GPIOs available in
the system.

This means that each userspace application, either running as a primary
or a secondary, should be able to control a set of distinct GPIOs.

In rare cases where multiple processes need to control the same set of
GPIOs userspace application is responsible for synchronizing accesses.

Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
v3:
- make gpiochip number unsigned
- do not hardcode length of PMD cmd line parameters buffer
- fix unchecked sscanf return value warning

 doc/guides/rawdevs/cnxk_gpio.rst  |   7 ++
 drivers/raw/cnxk_gpio/cnxk_gpio.c | 188 ++++++++++++++++++++----------
 2 files changed, 134 insertions(+), 61 deletions(-)

--
2.34.1
  

Patch

diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst
index adff535a77..848ad329e7 100644
--- a/doc/guides/rawdevs/cnxk_gpio.rst
+++ b/doc/guides/rawdevs/cnxk_gpio.rst
@@ -21,6 +21,7 @@  Following features are available:
 - set GPIO edge that triggers interrupt
 - set GPIO active low
 - register interrupt handler for specific GPIO
+- multiprocess aware

 Requirements
 ------------
@@ -30,6 +31,12 @@  for installing interrupt handlers for low latency signal processing.

 Driver is shipped with Marvell SDK.

+Limitations
+-----------
+
+In multiprocess mode user-space application must ensure no GPIO sharing across
+processes takes place.
+
 Device Setup
 ------------

diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index e2907c18b5..c480711942 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -19,6 +19,12 @@ 

 #define CNXK_GPIO_BUFSZ 128
 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio"
+#define CNXK_GPIO_PARAMS_MZ_NAME "cnxk_gpio_params_mz"
+
+struct cnxk_gpio_params {
+	unsigned int num;
+	char allowlist[];
+};

 static const char *const cnxk_gpio_args[] = {
 #define CNXK_GPIO_ARG_GPIOCHIP "gpiochip"
@@ -28,8 +34,6 @@  static const char *const cnxk_gpio_args[] = {
 	NULL
 };

-static char *allowlist;
-
 static void
 cnxk_gpio_format_name(char *name, size_t len)
 {
@@ -44,78 +48,138 @@  cnxk_gpio_filter_gpiochip(const struct dirent *dirent)
 	return !strncmp(dirent->d_name, pattern, strlen(pattern));
 }

-static void
-cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip)
+static int
+cnxk_gpio_set_defaults(struct cnxk_gpio_params *params)
 {
 	struct dirent **namelist;
-	int n;
+	int ret = 0, n;

 	n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip,
 		    alphasort);
 	if (n < 0 || n == 0)
-		return;
+		return -ENODEV;
+
+	if (sscanf(namelist[0]->d_name, "gpiochip%d", &params->num) != 1)
+		ret = -EINVAL;

-	sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num);
 	while (n--)
 		free(namelist[n]);
 	free(namelist);
+
+	return ret;
 }

 static int
 cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value,
 			     void *extra_args)
 {
-	long val;
+	unsigned long val;

 	errno = 0;
-	val = strtol(value, NULL, 10);
+	val = strtoul(value, NULL, 10);
 	if (errno)
 		return -errno;

-	*(int *)extra_args = (int)val;
+	*(unsigned int *)extra_args = val;

 	return 0;
 }

 static int
-cnxk_gpio_parse_arg_allowlist(const char *key __rte_unused, const char *value,
-			      void *extra_args __rte_unused)
+cnxk_gpio_parse_arg_allowlist(const char *key __rte_unused, const char *value, void *extra_args)
 {
-	allowlist = strdup(value);
-	if (!allowlist)
-		return -ENOMEM;
+	*(const char **)extra_args = value;
+
+	return 0;
+}
+
+static int
+cnxk_gpio_params_restore(struct cnxk_gpio_params **params)
+{
+	const struct rte_memzone *mz;
+
+	mz = rte_memzone_lookup(CNXK_GPIO_PARAMS_MZ_NAME);
+	if (!mz)
+		return -ENODEV;
+
+	*params = mz->addr;

 	return 0;
 }

+static struct cnxk_gpio_params *
+cnxk_gpio_params_reserve(size_t len)
+{
+	const struct rte_memzone *mz;
+
+	mz = rte_memzone_reserve(CNXK_GPIO_PARAMS_MZ_NAME, len, rte_socket_id(), 0);
+	if (!mz)
+		return NULL;
+
+	return mz->addr;
+}
+
+static void
+cnxk_gpio_params_release(void)
+{
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		rte_memzone_free(rte_memzone_lookup(CNXK_GPIO_PARAMS_MZ_NAME));
+}
+
 static int
-cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, const char *args)
+cnxk_gpio_parse_arg(struct rte_kvargs *kvlist, const char *arg, arg_handler_t handler, void *data)
 {
+	int ret;
+
+	ret = rte_kvargs_count(kvlist, arg);
+	if (ret == 0)
+		return 0;
+	if (ret > 1)
+		return -EINVAL;
+
+	return rte_kvargs_process(kvlist, arg, handler, data) ? -EIO : 1;
+}
+
+static int
+cnxk_gpio_parse_store_args(struct cnxk_gpio_params **params, const char *args)
+{
+	size_t len = sizeof(**params);
+	const char *allowlist = NULL;
 	struct rte_kvargs *kvlist;
 	int ret;

 	kvlist = rte_kvargs_parse(args, cnxk_gpio_args);
-	if (!kvlist)
-		return 0;
+	if (!kvlist) {
+		*params = cnxk_gpio_params_reserve(len);
+		if (!*params)
+			return -ENOMEM;

-	ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP);
-	if (ret == 1) {
-		ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP,
-					 cnxk_gpio_parse_arg_gpiochip,
-					 &gpiochip->num);
+		ret = cnxk_gpio_set_defaults(*params);
 		if (ret)
 			goto out;
-	}

-	ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_ALLOWLIST);
-	if (ret == 1) {
-		ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_ALLOWLIST,
-					 cnxk_gpio_parse_arg_allowlist, NULL);
-		if (ret)
-			goto out;
+		return 0;
 	}

-	ret = 0;
+	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_ALLOWLIST, cnxk_gpio_parse_arg_allowlist,
+				  &allowlist);
+	if (ret < 0)
+		goto out;
+
+	if (allowlist)
+		len += strlen(allowlist) + 1;
+
+	*params = cnxk_gpio_params_reserve(len);
+	if (!(*params))
+		return -ENOMEM;
+
+	strlcpy((*params)->allowlist, allowlist, strlen(allowlist) + 1);
+
+	ret = cnxk_gpio_parse_arg(kvlist, CNXK_GPIO_ARG_GPIOCHIP, cnxk_gpio_parse_arg_gpiochip,
+				  &(*params)->num);
+	if (ret == 0)
+		ret = cnxk_gpio_set_defaults(*params);
+
 out:
 	rte_kvargs_free(kvlist);

@@ -123,7 +187,7 @@  cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, const char *args)
 }

 static int
-cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip)
+cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip, char *allowlist)
 {
 	int i, ret, val, queue = 0;
 	char *token;
@@ -133,6 +197,12 @@  cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip)
 	if (!list)
 		return -ENOMEM;

+	allowlist = strdup(allowlist);
+	if (!allowlist) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	/* replace brackets with something meaningless for strtol() */
 	allowlist[0] = ' ';
 	allowlist[strlen(allowlist) - 1] = ' ';
@@ -166,11 +236,13 @@  cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip)
 			list[queue++] = val;
 	} while ((token = strtok(NULL, ",")));

+	free(allowlist);
 	gpiochip->allowlist = list;
 	gpiochip->num_queues = queue;

 	return 0;
 out:
+	free(allowlist);
 	rte_free(list);

 	return ret;
@@ -562,8 +634,7 @@  cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf)
 		*(int *)rsp = val;
 		break;
 	case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ:
-		ret = cnxk_gpio_register_irq(gpio,
-					     (struct cnxk_gpio_irq *)msg->data);
+		ret = cnxk_gpio_register_irq(gpio, (struct cnxk_gpio_irq *)msg->data);
 		break;
 	case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ:
 		ret = cnxk_gpio_unregister_irq(gpio);
@@ -659,17 +730,14 @@  static int
 cnxk_gpio_probe(struct rte_vdev_device *dev)
 {
 	char name[RTE_RAWDEV_NAME_MAX_LEN];
+	struct cnxk_gpio_params *params;
 	struct cnxk_gpiochip *gpiochip;
 	struct rte_rawdev *rawdev;
 	char buf[CNXK_GPIO_BUFSZ];
 	int ret;

-	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
-		return 0;
-
 	cnxk_gpio_format_name(name, sizeof(name));
-	rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip),
-					 rte_socket_id());
+	rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), rte_socket_id());
 	if (!rawdev) {
 		RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name);
 		return -ENOMEM;
@@ -678,22 +746,26 @@  cnxk_gpio_probe(struct rte_vdev_device *dev)
 	rawdev->dev_ops = &cnxk_gpio_rawdev_ops;
 	rawdev->device = &dev->device;
 	rawdev->driver_name = dev->device.name;
-
 	gpiochip = rawdev->dev_private;
-	cnxk_gpio_set_defaults(gpiochip);

-	/* defaults may be overwritten by this call */
-	ret = cnxk_gpio_parse_args(gpiochip, rte_vdev_device_args(dev));
-	if (ret)
-		goto out;
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+		ret = cnxk_gpio_parse_store_args(&params, rte_vdev_device_args(dev));
+		if (ret < 0)
+			goto out;
+	} else {
+		ret = cnxk_gpio_params_restore(&params);
+		if (ret)
+			goto out;
+	}
+
+	gpiochip->num = params->num;

 	ret = cnxk_gpio_irq_init(gpiochip);
 	if (ret)
 		goto out;

 	/* read gpio base */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH,
-		 gpiochip->num);
+	snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num);
 	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base);
 	if (ret) {
 		RTE_LOG(ERR, PMD, "failed to read %s", buf);
@@ -701,8 +773,7 @@  cnxk_gpio_probe(struct rte_vdev_device *dev)
 	}

 	/* read number of available gpios */
-	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH,
-		 gpiochip->num);
+	snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, gpiochip->num);
 	ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios);
 	if (ret) {
 		RTE_LOG(ERR, PMD, "failed to read %s", buf);
@@ -710,16 +781,13 @@  cnxk_gpio_probe(struct rte_vdev_device *dev)
 	}
 	gpiochip->num_queues = gpiochip->num_gpios;

-	if (allowlist) {
-		ret = cnxk_gpio_parse_allowlist(gpiochip);
-		free(allowlist);
-		allowlist = NULL;
-		if (ret)
-			goto out;
+	ret = cnxk_gpio_parse_allowlist(gpiochip, params->allowlist);
+	if (ret) {
+		RTE_LOG(ERR, PMD, "failed to parse allowed gpios\n");
+		goto out;
 	}

-	gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios,
-				     sizeof(struct cnxk_gpio *), 0);
+	gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, sizeof(struct cnxk_gpio *), 0);
 	if (!gpiochip->gpios) {
 		RTE_LOG(ERR, PMD, "failed to allocate gpios memory");
 		ret = -ENOMEM;
@@ -728,8 +796,8 @@  cnxk_gpio_probe(struct rte_vdev_device *dev)

 	return 0;
 out:
-	free(allowlist);
 	rte_free(gpiochip->allowlist);
+	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);

 	return ret;
@@ -746,9 +814,6 @@  cnxk_gpio_remove(struct rte_vdev_device *dev)

 	RTE_SET_USED(dev);

-	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
-		return 0;
-
 	cnxk_gpio_format_name(name, sizeof(name));
 	rawdev = rte_rawdev_pmd_get_named_dev(name);
 	if (!rawdev)
@@ -769,6 +834,7 @@  cnxk_gpio_remove(struct rte_vdev_device *dev)
 	rte_free(gpiochip->allowlist);
 	rte_free(gpiochip->gpios);
 	cnxk_gpio_irq_fini();
+	cnxk_gpio_params_release();
 	rte_rawdev_pmd_release(rawdev);

 	return 0;