From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1E1392046BC for ; Sat, 15 Mar 2025 16:41:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056907; cv=none; b=Sbf0NlNzOVokitUiMrKuMhculUVGR9owU0zLThddvG48noEqyK0B/HBQIFICBdi5wi25sDO+sX25gvnTbEObEdqVqBtwL+v0OGW2ck+DYtBSxAIZozieFzMCShu1hQAxLo3GGUiajrA6agBiE74Jv8mxj+K1KXAnfaYjNpqc8vk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056907; c=relaxed/simple; bh=CXXjPitrP+xrfPteKcdz/uPauSvRoyrwwz62x5UXz7s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u5fgXjwT0kBcMGyIFhkyvGQfXTzbOZZUzD+Ri9aG/din/lOfOiX0mlWdJQQgPqHJz4CayYFYse8Qu5Ak7RIvbZ+Mb7eA9BCyZ8mmWVI6vmJm6guvrvDmNXHxLR2YzHTPXYQggrdlNK/y7PdglIwo9FZKoORupcn8MXyx2BVszpY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=C1d++y8x; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="C1d++y8x" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id B0D303F31D for ; Sat, 15 Mar 2025 16:41:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056896; bh=82ap22DORRQTX0OBb3Kwtetq95gagD3gpR37XzEzCWM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C1d++y8xYuzqtG3dmg6DHfkvzzU5/UNk85/ziwz48apEufe6rMFvG8RoAsA7zK1b8 L/6tSTwfgaDXyL6WZZcIzEeWHqS8tyMBwkLgZJbv761bezZIkt9nSMPKDOjGIUVT8A gXCqPw4BI17UKYrfXcFg1oVLe49yvjDnADJg70wycdL/QJlB/9dFgZxE01Be4JwyTf rdpP6odaBe68v84eIM8O+RC9/wowzLphiL0S32fajifttpBiz3Onetc1W/ZmnWDsIH +zAVwRrMRb2Au6Z5aeFYrWGk5e2uguRNWKd+QH9FddXNeu62sBEF61IFEKBGeMGbiv dJJeASBpNzf3Q== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2233b764fc8so52940545ad.3 for ; Sat, 15 Mar 2025 09:41:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056895; x=1742661695; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=82ap22DORRQTX0OBb3Kwtetq95gagD3gpR37XzEzCWM=; b=B2MH/lcDVeYqudX/dwMZoVYXBhJrtkqUjraS2lv/XHIwYcYfddDuXWPa8r9REtASUH cN7mIn7EVxOxaQMvcjQ7nuUcr+WM7q8ESaGrnujidX3SmJ1AoK/1yFZXYqKnOUVX5D6Z duqA65hOsVl+Aeeh/NYzZHvm784rpesHG894eVbg06gTAJpGDktUBg3FBa/+cVkoa2W4 74etjMlopQISAgYbj2Bsp6ajryKM4uyqHD6grnA/MHd8TrMIKR0pA9OGiMQv0wGAMV53 DeOp7c6efobUFYYg5MYxQdX6Z0K+3kk+xYz9KWejbnVeeas/jtaZ9bmmQTTgFVyeKNog Ex3A== X-Forwarded-Encrypted: i=1; AJvYcCVyR734e4aztSIT6s6BdHN/75mUBKEJDLRvrx30pt/TAxW8xvZkyDTD7zmA5Hu4KmKtmrJiZpjL8vw3feA=@vger.kernel.org X-Gm-Message-State: AOJu0Yxf3Nol8NtRZDrEXroyafswpcLvv7EP+4LFSZAeJoKqTZGqmETL 5kWC7ak5QRhab3VQf7ipwK6B5FteCI6BIbkEFMEyNbT63XCiDipPP78cnn//u4RVVZyRvjLdB4L xAa8tzLmxYfrs5lhC70MZ2rKbU7kegw6YF7a/l6DAZ9EqSprAerqKeayL1Egf38WPBM8vyEK9gn ONuQ== X-Gm-Gg: ASbGncvhso6iQKncRkFN9xHyaJyKtwOBNFqggF7AIvHxztnuPfs41HqRjQoDftgoAaZ jRdWUd9G5xgOnsHQ6B7/G0J/f1dyh2c5fwq7uy219bQwIJceiPj5l70fX2IBCLPS4VABGDcYCaN VjnqR2DfN/uDI3msWCpAWtLP0rrICL+SuTeFXi7CXI6+Fl8jgHebjzUnaMmUywhHr/f82uXYUBO VDESgoEBSlov10v8QOSHLXNVXTmGa1u0xuGXcUYrnbEXvSUkdBDcvQTOZTdrZkdf8d0aStCZdlm PHqVCWrrsBZWOd8l X-Received: by 2002:a05:6a00:18a3:b0:736:57cb:f2aa with SMTP id d2e1a72fcca58-737223b756dmr6781976b3a.13.1742056895269; Sat, 15 Mar 2025 09:41:35 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEXPeNwzj5+JJOIIiToGLUpE6fZd83ok5TeonsA3qTr3u41t5dDANyAsspOj3sjMjW2QSIZ6A== X-Received: by 2002:a05:6a00:18a3:b0:736:57cb:f2aa with SMTP id d2e1a72fcca58-737223b756dmr6781960b3a.13.1742056894873; Sat, 15 Mar 2025 09:41:34 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:34 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 1/9] gpio: aggregator: protect driver attr handlers against module unload Date: Sun, 16 Mar 2025 01:41:15 +0900 Message-ID: <20250315164123.1855142-2-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Both new_device_store and delete_device_store touch module global resources (e.g. gpio_aggregator_lock). To prevent race conditions with module unload, a reference needs to be held. Add try_module_get() in these handlers. For new_device_store, this eliminates what appears to be the most dangerous scenario: if an id is allocated from gpio_aggregator_idr but platform_device_register has not yet been called or completed, a concurrent module unload could fail to unregister/delete the device, leaving behind a dangling platform device/GPIO forwarder. This can result in various issues. The following simple reproducer demonstrates these problems: #!/bin/bash while :; do # note: whether 'gpiochip0 0' exists or not does not matter. echo 'gpiochip0 0' > /sys/bus/platform/drivers/gpio-aggregator/new_devi= ce done & while :; do modprobe gpio-aggregator modprobe -r gpio-aggregator done & wait Starting with the following warning, several kinds of warnings will appear and the system may become unstable: ------------[ cut here ]------------ list_del corruption, ffff888103e2e980->next is LIST_POISON1 (dead00000000= 0100) WARNING: CPU: 1 PID: 1327 at lib/list_debug.c:56 __list_del_entry_valid_o= r_report+0xa3/0x120 [...] RIP: 0010:__list_del_entry_valid_or_report+0xa3/0x120 [...] Call Trace: ? __list_del_entry_valid_or_report+0xa3/0x120 ? __warn.cold+0x93/0xf2 ? __list_del_entry_valid_or_report+0xa3/0x120 ? report_bug+0xe6/0x170 ? __irq_work_queue_local+0x39/0xe0 ? handle_bug+0x58/0x90 ? exc_invalid_op+0x13/0x60 ? asm_exc_invalid_op+0x16/0x20 ? __list_del_entry_valid_or_report+0xa3/0x120 gpiod_remove_lookup_table+0x22/0x60 new_device_store+0x315/0x350 [gpio_aggregator] kernfs_fop_write_iter+0x137/0x1f0 vfs_write+0x262/0x430 ksys_write+0x60/0xd0 do_syscall_64+0x6c/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e [...] ---[ end trace 0000000000000000 ]--- Fixes: 828546e24280 ("gpio: Add GPIO Aggregator") Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index e799599a06a1..d232ea865356 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -119,10 +119,15 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, struct platform_device *pdev; int res, id; =20 + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + /* kernfs guarantees string termination, so count + 1 is safe */ aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) - return -ENOMEM; + if (!aggr) { + res =3D -ENOMEM; + goto put_module; + } =20 memcpy(aggr->args, buf, count + 1); =20 @@ -161,6 +166,7 @@ static ssize_t new_device_store(struct device_driver *d= river, const char *buf, } =20 aggr->pdev =3D pdev; + module_put(THIS_MODULE); return count; =20 remove_table: @@ -175,6 +181,8 @@ static ssize_t new_device_store(struct device_driver *d= river, const char *buf, kfree(aggr->lookups); free_ga: kfree(aggr); +put_module: + module_put(THIS_MODULE); return res; } =20 @@ -203,13 +211,19 @@ static ssize_t delete_device_store(struct device_driv= er *driver, if (error) return error; =20 + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + mutex_lock(&gpio_aggregator_lock); aggr =3D idr_remove(&gpio_aggregator_idr, id); mutex_unlock(&gpio_aggregator_lock); - if (!aggr) + if (!aggr) { + module_put(THIS_MODULE); return -ENOENT; + } =20 gpio_aggregator_free(aggr); + module_put(THIS_MODULE); return count; } static DRIVER_ATTR_WO(delete_device); --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20566204845 for ; Sat, 15 Mar 2025 16:41:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056908; cv=none; b=OkDPumilqT/cuqiIF246lA6vW1seuv6dWz6kaOQeEVdyH3o8ZLKtPltpSqQ8LkPTuhRnCB9kc7N0Q1JvgIrBH61g45gM/F62TpH10Bgjf47fYogGf87OeXshCNLYM3ASKPzqn4p50LFGE58WHuCyLJA0loLTGXo7GsLcxjR2NqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056908; c=relaxed/simple; bh=QKzGBY006K35i0VUre3m/YXkXfWXlIbWjW5345Q28Sw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mqJq9V8ZCR0Cb82AD05PbjipPAKyjeS13Wy7XdvrwPFMGOCTV4eIND6Guz2ihhxIzXjy/Ox6iRswLbR+GyFfoVB0XCEiY99WDBNIo/C0A13HyhN7QnPQ8unddlWtwb6laqdQr9OqTk8VA74HLVcE9CK/ZlcY8y4N70KlA/yjtXU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=DNnm0WRs; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="DNnm0WRs" Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 204E43F1E0 for ; Sat, 15 Mar 2025 16:41:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056899; bh=gYD77nW/waFXgRcL7RZiNMF3p8f4KzCk7t7Ss3jccwc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DNnm0WRs+IOVhT5AxX8bUybsJ5rsb3p1PMGwI83oExzqX8VI78eG33UdbLOExsIxB iUuuVkEPUYQ+afmHyiJk0epxd9FyhOVfSDToTOQ7c2BYc9kH61X+0iSWeMoQhxC+ni bJnpR+7Zq7JHN6+1eL5aKcobXdzMH78Gu+RQRsspsBPKyNdjsBMt+26A5OHmFCfivB cA/NB/SyN+bh+VRrSZQBAt0LErtez9aYo4LELonT2JHKBv5ylV6PY0k/kVbzBQCk8z aihRo7hXFVE2RfoH9FLaZTA0JiMFGK3oqFg6LuAqxyAhoJKPS7Iq89bkgPpAkdzDJm kv63FqcQGB9Hw== Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-2ff8c5d185aso2281543a91.1 for ; Sat, 15 Mar 2025 09:41:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056898; x=1742661698; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=gYD77nW/waFXgRcL7RZiNMF3p8f4KzCk7t7Ss3jccwc=; b=cbEoX45tFJUFY3gpRFHCh0oU9rh6ei5g0PJfYB2vpWtXMeNk0+I4VuEvXaaXoavVFP 0+Of8I5aUoJtGaqPWSBJlu4Tc7GhOOCaeyIIcd95BwbqSJcYAa6hdYyXex3u13XPgOci T7CopLPT5rC1JRoc4Qo4vl1DE/m+I3U4C8DKUKpN0Kp10WX1rv0RYT1v2aCOmTq2qn9i O3eSKUBpEp6UlP5Kc6KI9wRoQMr/qlQLLZbddqHGWWEoHdgo0ksQtHNMaZ1uBpEWpn9z 5OTWnZ4yIDPvTVYGLygJxMUXao7qdXM47gpQzc2v9/D/Dye/wWf8JOz5Bf8Wziv+PHYD drLg== X-Forwarded-Encrypted: i=1; AJvYcCXWDgOlLcqFb/i1nn8QqZ9pQ0sPVj4sV7ljb5Ie5rx2jTRyOAxU8j42CDgfo8VAczjlQih1UjzMfaFm8FM=@vger.kernel.org X-Gm-Message-State: AOJu0YwOdQRInvQInq6/5I9r7q+yJYXJy1doStBidwfATzpf5Yeg4eYL vUuhiV+fDzlucI1reGsa8Q5e8PalYG2CJ6/JQIyk1L2/QMDPseyil7bWYw/Iuf5YovHiPLLOGbP H+ARrTrU+O+aowIa31kfKye+GD1/feexxkIiC9Jo5Hy520CsXFH9N15GnrD2HbjBhiLeGzKXDFq kcLA== X-Gm-Gg: ASbGncsB1Lrbe9oIDXpUZ/40Qb2DiF9Q3Imr7EeQh85481aXeAcPDEQc1X53xg58xXv Fv2ldx4gL0NHxEAqCVDIZ5lDRgHx2zJyX/mS0WZCdTINOftmJiSwwSaenKcw2HeMHrrrgvLkkSf 2okDw8EPM6t2Grg+K32zBMbf85LhhHphInk/ZgUs8VUL/Ldgnyv8hL/U0EQ6pDn0qwtRPhBijtw 6aFINwlNAjUtbWKIsIdcpA1h1T70SBKHhmz+lVmAKjUHpA4XImSNu3a1bKW5IKp8NPk6UHKTAQ0 owkDbllUyZZ+IXsb X-Received: by 2002:a05:6a00:982:b0:736:43d6:f008 with SMTP id d2e1a72fcca58-7372235abb3mr8709249b3a.12.1742056897665; Sat, 15 Mar 2025 09:41:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEyWRch+DZlOrDcTTNMtsFblIYnc5TdxZVbq/yNbPFKS8wtP5jPBVMjXtbMdb8ayyf/SXsn4w== X-Received: by 2002:a05:6a00:982:b0:736:43d6:f008 with SMTP id d2e1a72fcca58-7372235abb3mr8709229b3a.12.1742056897288; Sat, 15 Mar 2025 09:41:37 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:36 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 2/9] gpio: aggregator: reorder functions to prepare for configfs introduction Date: Sun, 16 Mar 2025 01:41:16 +0900 Message-ID: <20250315164123.1855142-3-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Reorder functions in drivers/gpio/gpio-aggregator.c to prepare for the configfs-based interface additions in subsequent commits. Arrange the code so that the configfs implementations will appear above the existing sysfs-specific code, since the latter will partly depend on the configfs interface implementations when it starts to expose the settings to configfs. The order in drivers/gpio/gpio-aggregator.c will be as follows: * Basic gpio_aggregator/gpio_aggregator_line representations * Common utility functions * GPIO Forwarder implementations * Configfs interface implementations * Sysfs interface implementations * Platform device implementations * Module init/exit implementations This separate commit ensures a clean diff for the subsequent commits. No functional change. Reviewed-by: Geert Uytterhoeven Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 380 +++++++++++++++++---------------- 1 file changed, 192 insertions(+), 188 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index d232ea865356..e026deb4ac64 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -61,194 +61,6 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, = const char *key, return 0; } =20 -static int aggr_parse(struct gpio_aggregator *aggr) -{ - char *args =3D skip_spaces(aggr->args); - char *name, *offsets, *p; - unsigned int i, n =3D 0; - int error =3D 0; - - unsigned long *bitmap __free(bitmap) =3D - bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); - if (!bitmap) - return -ENOMEM; - - args =3D next_arg(args, &name, &p); - while (*args) { - args =3D next_arg(args, &offsets, &p); - - p =3D get_options(offsets, 0, &error); - if (error =3D=3D 0 || *p) { - /* Named GPIO line */ - error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); - if (error) - return error; - - name =3D offsets; - continue; - } - - /* GPIO chip + offset(s) */ - error =3D bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); - if (error) { - pr_err("Cannot parse %s: %d\n", offsets, error); - return error; - } - - for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error =3D aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } - - args =3D next_arg(args, &name, &p); - } - - if (!n) { - pr_err("No GPIOs specified\n"); - return -EINVAL; - } - - return 0; -} - -static ssize_t new_device_store(struct device_driver *driver, const char *= buf, - size_t count) -{ - struct gpio_aggregator *aggr; - struct platform_device *pdev; - int res, id; - - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - /* kernfs guarantees string termination, so count + 1 is safe */ - aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res =3D -ENOMEM; - goto put_module; - } - - memcpy(aggr->args, buf, count + 1); - - aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), - GFP_KERNEL); - if (!aggr->lookups) { - res =3D -ENOMEM; - goto free_ga; - } - - mutex_lock(&gpio_aggregator_lock); - id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res =3D id; - goto free_table; - } - - aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); - if (!aggr->lookups->dev_id) { - res =3D -ENOMEM; - goto remove_idr; - } - - res =3D aggr_parse(aggr); - if (res) - goto free_dev_id; - - gpiod_add_lookup_table(aggr->lookups); - - pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); - if (IS_ERR(pdev)) { - res =3D PTR_ERR(pdev); - goto remove_table; - } - - aggr->pdev =3D pdev; - module_put(THIS_MODULE); - return count; - -remove_table: - gpiod_remove_lookup_table(aggr->lookups); -free_dev_id: - kfree(aggr->lookups->dev_id); -remove_idr: - mutex_lock(&gpio_aggregator_lock); - idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); -free_table: - kfree(aggr->lookups); -free_ga: - kfree(aggr); -put_module: - module_put(THIS_MODULE); - return res; -} - -static DRIVER_ATTR_WO(new_device); - -static void gpio_aggregator_free(struct gpio_aggregator *aggr) -{ - platform_device_unregister(aggr->pdev); - gpiod_remove_lookup_table(aggr->lookups); - kfree(aggr->lookups->dev_id); - kfree(aggr->lookups); - kfree(aggr); -} - -static ssize_t delete_device_store(struct device_driver *driver, - const char *buf, size_t count) -{ - struct gpio_aggregator *aggr; - unsigned int id; - int error; - - if (!str_has_prefix(buf, DRV_NAME ".")) - return -EINVAL; - - error =3D kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); - if (error) - return error; - - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - mutex_lock(&gpio_aggregator_lock); - aggr =3D idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { - module_put(THIS_MODULE); - return -ENOENT; - } - - gpio_aggregator_free(aggr); - module_put(THIS_MODULE); - return count; -} -static DRIVER_ATTR_WO(delete_device); - -static struct attribute *gpio_aggregator_attrs[] =3D { - &driver_attr_new_device.attr, - &driver_attr_delete_device.attr, - NULL -}; -ATTRIBUTE_GROUPS(gpio_aggregator); - -static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) -{ - gpio_aggregator_free(p); - return 0; -} - -static void __exit gpio_aggregator_remove_all(void) -{ - mutex_lock(&gpio_aggregator_lock); - idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); - idr_destroy(&gpio_aggregator_idr); - mutex_unlock(&gpio_aggregator_lock); -} - =20 /* * GPIO Forwarder @@ -583,6 +395,184 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struc= t device *dev, } =20 =20 +/* + * Sysfs interface + */ +static int aggr_parse(struct gpio_aggregator *aggr) +{ + char *args =3D skip_spaces(aggr->args); + char *name, *offsets, *p; + unsigned int i, n =3D 0; + int error =3D 0; + + unsigned long *bitmap __free(bitmap) =3D + bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; + + args =3D next_arg(args, &name, &p); + while (*args) { + args =3D next_arg(args, &offsets, &p); + + p =3D get_options(offsets, 0, &error); + if (error =3D=3D 0 || *p) { + /* Named GPIO line */ + error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); + if (error) + return error; + + name =3D offsets; + continue; + } + + /* GPIO chip + offset(s) */ + error =3D bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + return error; + } + + for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + error =3D aggr_add_gpio(aggr, name, i, &n); + if (error) + return error; + } + + args =3D next_arg(args, &name, &p); + } + + if (!n) { + pr_err("No GPIOs specified\n"); + return -EINVAL; + } + + return 0; +} + +static ssize_t new_device_store(struct device_driver *driver, const char *= buf, + size_t count) +{ + struct gpio_aggregator *aggr; + struct platform_device *pdev; + int res, id; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + /* kernfs guarantees string termination, so count + 1 is safe */ + aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); + if (!aggr) { + res =3D -ENOMEM; + goto put_module; + } + + memcpy(aggr->args, buf, count + 1); + + aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) { + res =3D -ENOMEM; + goto free_ga; + } + + mutex_lock(&gpio_aggregator_lock); + id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); + mutex_unlock(&gpio_aggregator_lock); + + if (id < 0) { + res =3D id; + goto free_table; + } + + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + if (!aggr->lookups->dev_id) { + res =3D -ENOMEM; + goto remove_idr; + } + + res =3D aggr_parse(aggr); + if (res) + goto free_dev_id; + + gpiod_add_lookup_table(aggr->lookups); + + pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); + if (IS_ERR(pdev)) { + res =3D PTR_ERR(pdev); + goto remove_table; + } + + aggr->pdev =3D pdev; + module_put(THIS_MODULE); + return count; + +remove_table: + gpiod_remove_lookup_table(aggr->lookups); +free_dev_id: + kfree(aggr->lookups->dev_id); +remove_idr: + mutex_lock(&gpio_aggregator_lock); + idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); +free_table: + kfree(aggr->lookups); +free_ga: + kfree(aggr); +put_module: + module_put(THIS_MODULE); + return res; +} + +static DRIVER_ATTR_WO(new_device); + +static void gpio_aggregator_free(struct gpio_aggregator *aggr) +{ + platform_device_unregister(aggr->pdev); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); + kfree(aggr); +} + +static ssize_t delete_device_store(struct device_driver *driver, + const char *buf, size_t count) +{ + struct gpio_aggregator *aggr; + unsigned int id; + int error; + + if (!str_has_prefix(buf, DRV_NAME ".")) + return -EINVAL; + + error =3D kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); + if (error) + return error; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + mutex_lock(&gpio_aggregator_lock); + aggr =3D idr_remove(&gpio_aggregator_idr, id); + mutex_unlock(&gpio_aggregator_lock); + if (!aggr) { + module_put(THIS_MODULE); + return -ENOENT; + } + + gpio_aggregator_free(aggr); + module_put(THIS_MODULE); + return count; +} +static DRIVER_ATTR_WO(delete_device); + +static struct attribute *gpio_aggregator_attrs[] =3D { + &driver_attr_new_device.attr, + &driver_attr_delete_device.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpio_aggregator); + + /* * GPIO Aggregator platform device */ @@ -640,6 +630,20 @@ static struct platform_driver gpio_aggregator_driver = =3D { }, }; =20 +static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +{ + gpio_aggregator_free(p); + return 0; +} + +static void __exit gpio_aggregator_remove_all(void) +{ + mutex_lock(&gpio_aggregator_lock); + idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); + idr_destroy(&gpio_aggregator_idr); + mutex_unlock(&gpio_aggregator_lock); +} + static int __init gpio_aggregator_init(void) { return platform_driver_register(&gpio_aggregator_driver); --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D2051DD87D for ; Sat, 15 Mar 2025 16:41:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056905; cv=none; b=WI4u3Mc1+/XfAnzhACF2zIoj2YH5rhasYqkMqRQKUpfXNasBvgqcYuaja2Ebxbx6fay/Tb70wCKDtMhTsrJf5idvkzPW1nnA0BOlziQoI1irnW0fQu7Qv4JYZX3bqUIF68DyhaxIPmVTBpqD/0I19hmiCwkGS3Ai72UY7B1NH6Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056905; c=relaxed/simple; bh=Y/trqAHztonL9N2oBMTBUS2AITNCJpyiU7FuUoJKaI0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TCkdzbZn1mqgu/eZANEEtJIiRq9L/jPCp+ep3V8/cgYuQq/TYo2AhbgE2Z3Ne6gpkVlncTtxgKhbKHkHAneiidTQH4H7j8TBNTK79hdO+LzwiIvW4fZcPeudtpTo1pZHa4nUlInmonXQLtoVxB9m6frK/WfM1ARs5DKnYSTf8rU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=rix3luJf; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="rix3luJf" Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 3D0B13F5AE for ; Sat, 15 Mar 2025 16:41:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056901; bh=YqI63cnlFxHvHbiIKycy1SSwr0GW+0W28cLAktX9jRk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rix3luJfUpHKi4gJeMAcW5+jeAaryLG1GVMfoq17CT8aHnQipCZ7wK3sN6Ctjkics w/i1FBW60vLp/o4hqmvwEH3g+hxwkp+IB1nDDPlsS93z80j78SIAH3LXBAFjTMI3+r UJvpfkc4eXTqXU1UfA/RtEaAnIeBHk3roN4qn07RY5lj9K6AphLALf6P5go4ozDZ7o dWYpJjmqzS3GsI5VS1dba2Snk6fYJV4CFX0vNtUCVxOXFjwYWeub5VCwoW95wZU/Nz l5JuveNM6wP0Xu1EDpj7bYnKzvXzNx7Wyea6+QUJ0Ju5SU32oNPdXPk28eHs82YMHc 04N88tkWD9Mbw== Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-225107fbdc7so49444565ad.0 for ; Sat, 15 Mar 2025 09:41:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056900; x=1742661700; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YqI63cnlFxHvHbiIKycy1SSwr0GW+0W28cLAktX9jRk=; b=SZ7idu5c9QkpfK71QMz+TGG7ufa3Lu52rpqm1JgOqW9y9VJJMNgosNfIH2GPElSjDH BNtFiFNNLnwutt55HeLO2v8hzLnveSth996LLVyRSQHRfeFB29uce0ET0qwW5GwoCmZl z/2LfIB2349AeVMRL8RLD6HP5RbPTzZaIjVDQey+/bpoR61aYUguT48xtP3vlOP0MQhq z1jT6Mm8ylBmCjA6nHZQ/4UI1geNdQ/PAEHsGk/tl3c0OaelD7TiC0wbxNPzZ94Ajdn5 et0SFPrxmuFXkU8WjwvUBQpyvd4aBgSp1bW3C7Kz+zmS1nlx8wHjlC72AdfFq09LRSNZ DgTw== X-Forwarded-Encrypted: i=1; AJvYcCXEK8SBdaGcTSb44aKXOv5l4lVbkTaHKc5/V1y0vY/nQsj03yUdftQ3zSZAsTqh7sEsU/OXifajTjLXICk=@vger.kernel.org X-Gm-Message-State: AOJu0Yzo8xXBjMo9iNIQUnhuxmp63qEkKSoih0e7GWCrT1XOapfyNhcz wqVGrNHfLhWR0/S/4ffDCnMa/1mZYX0SJ66toVYYUtGRD3K9dZWtABQgeUpDUmpHA0U7tQ68J0e dCeFkl1QA0pT6MThNluWiRJ2L1n6zbcTvYFuo8zeQxhQdZvVUGDRpwY6/7xN76KeDOVoCmH7wBW dhTQ== X-Gm-Gg: ASbGncu+oQyPJYdtmUMW1nJvoUMFXn6e0QOv5ppJNk9VpYJalpIk8QnrDbogj3tmiDC zFQNFVV7gQE7KiWlwAn1y3WLpUyBThYokgIoNeSW7HDblSeVS0CN+fHdAep0wwM0VxIuIWCvTj7 5fHqHiP1tIw3sObqmUiF3jzd6DWQZ6MLJ4KGIKbKiLfWrba71R/Jsw9HDb0EVrGX0jMnyL//e5o lLrWxzytAvs3laR2WlLPTdqyREEcC3+cpmaYbKDSAblurhIRqTAF8UO/rBtDchUzKphIfirua7C R8wm24e74wnHGMrC X-Received: by 2002:a05:6a20:9c8d:b0:1f3:2a83:7548 with SMTP id adf61e73a8af0-1f5c13c9108mr8797266637.38.1742056899838; Sat, 15 Mar 2025 09:41:39 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFlQ0ZxJkbof5Sl7HVvOIKdRU7nWkglT9nuW0CmRzdrLz1arh/azPRtzobbcQ27o5qULxvlTw== X-Received: by 2002:a05:6a20:9c8d:b0:1f3:2a83:7548 with SMTP id adf61e73a8af0-1f5c13c9108mr8797247637.38.1742056899553; Sat, 15 Mar 2025 09:41:39 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:39 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 3/9] gpio: aggregator: add aggr_alloc()/aggr_free() Date: Sun, 16 Mar 2025 01:41:17 +0900 Message-ID: <20250315164123.1855142-4-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Prepare for the upcoming configfs interface. These functions will be used by both the existing sysfs interface and the new configfs interface, reducing code duplication. No functional change. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 58 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index e026deb4ac64..2692a31e01ac 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -36,12 +36,41 @@ struct gpio_aggregator { struct gpiod_lookup_table *lookups; struct platform_device *pdev; + int id; char args[]; }; =20 static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 +static int aggr_alloc(struct gpio_aggregator **aggr, size_t arg_size) +{ + struct gpio_aggregator *new __free(kfree) =3D NULL; + int ret; + + new =3D kzalloc(sizeof(*new) + arg_size, GFP_KERNEL); + if (!new) + return -ENOMEM; + + mutex_lock(&gpio_aggregator_lock); + ret =3D idr_alloc(&gpio_aggregator_idr, new, 0, 0, GFP_KERNEL); + mutex_unlock(&gpio_aggregator_lock); + if (ret < 0) + return ret; + + new->id =3D ret; + *aggr =3D no_free_ptr(new); + return 0; +} + +static void aggr_free(struct gpio_aggregator *aggr) +{ + mutex_lock(&gpio_aggregator_lock); + idr_remove(&gpio_aggregator_idr, aggr->id); + mutex_unlock(&gpio_aggregator_lock); + kfree(aggr); +} + static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, int hwnum, unsigned int *n) { @@ -454,17 +483,15 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, { struct gpio_aggregator *aggr; struct platform_device *pdev; - int res, id; + int res; =20 if (!try_module_get(THIS_MODULE)) return -ENOENT; =20 /* kernfs guarantees string termination, so count + 1 is safe */ - aggr =3D kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res =3D -ENOMEM; + res =3D aggr_alloc(&aggr, count + 1); + if (res) goto put_module; - } =20 memcpy(aggr->args, buf, count + 1); =20 @@ -475,19 +502,10 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, goto free_ga; } =20 - mutex_lock(&gpio_aggregator_lock); - id =3D idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); - - if (id < 0) { - res =3D id; - goto free_table; - } - - aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->= id); if (!aggr->lookups->dev_id) { res =3D -ENOMEM; - goto remove_idr; + goto free_table; } =20 res =3D aggr_parse(aggr); @@ -496,7 +514,7 @@ static ssize_t new_device_store(struct device_driver *d= river, const char *buf, =20 gpiod_add_lookup_table(aggr->lookups); =20 - pdev =3D platform_device_register_simple(DRV_NAME, id, NULL, 0); + pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); if (IS_ERR(pdev)) { res =3D PTR_ERR(pdev); goto remove_table; @@ -510,14 +528,10 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, gpiod_remove_lookup_table(aggr->lookups); free_dev_id: kfree(aggr->lookups->dev_id); -remove_idr: - mutex_lock(&gpio_aggregator_lock); - idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); free_table: kfree(aggr->lookups); free_ga: - kfree(aggr); + aggr_free(aggr); put_module: module_put(THIS_MODULE); return res; --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 21F2F20485F for ; Sat, 15 Mar 2025 16:41:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056909; cv=none; b=bGzt4pQL/No9rs3O+Yl1BepFfHRdy+Xv2i0vVnnvH5DIix8l77PjGn7Um5dXCq/vhqJM5mK8ep6iBW5hptxe10XsC9gg/9hH7WA5svXHSvlsXcILk/DX2GUsnTDhKOjv2V9NBL6wgVU46OgsRiOLivmMwDc8dyL/49uJ6Bk2HuM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056909; c=relaxed/simple; bh=SGhWbdOQRTPml3qeoDWxl9WY2xkEqq+CeTuQQc1k7Kk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cBdWPUGnZRwjKgoim4pvxtEtxpwaCdngUM+GvkMYZqdYr5ehip0GjefXRx1oqKQWhzRXAZKFWN3Qn6GSILGstj0ULzW0YSZootiLMuB2xxdAhVHo2WhdXbfcTRRaRSWqWsCotUNQDW2liueuLhlbYy+ILzvi1z3TCs3g0JYkR/k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=LcXwWQc+; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="LcXwWQc+" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 464683F27F for ; Sat, 15 Mar 2025 16:41:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056904; bh=u0Db2jgIvdRh2lyJr3UQ57N3a3Y/qyh0p8zexbk7hWI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LcXwWQc+jE2VCyDzJxsKBHsrJl1RLzGDJMQAMd2M0Q4scOYTTUjutTh1KJxVp7EJ3 zO78nzqZrvb+UlhupL/VJ1NTesmcTMkZYJePawpT8qYD8xkmXUDZwiTG4qjuINAEzn VuA6/eabGWMEysy9ZRLEB4qVS/oKq6ukUdnNBI4JXlrFMzG6ibdnTdKRC+lpYwvSty 0KC053rqVenhih0Wjaxgu4yZDBYF8BODrZqx+6m6y2FcC8w08/7eCd/s5YMiClzAs/ CcNixPVNGw0e1OeF6Mgny8j9um+5L2OmgTCcE1fbZfDWp3ypqf6pPDt3u9prC1rL6A ozvz2QSyYJ9hw== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-22403329f9eso47034975ad.3 for ; Sat, 15 Mar 2025 09:41:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056903; x=1742661703; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=u0Db2jgIvdRh2lyJr3UQ57N3a3Y/qyh0p8zexbk7hWI=; b=b6vwXFKg1XBPOiLm5SE3gDP1S+sixV6ihen1q+6zT/DA8Tcoa4PrJggA3Uq8Ni4N9m AbN1OhWnTi9z58Jr860HSdYs1BHBJ+G8ZkOlq1UbZyn+cCwF5IwgUwFM/hlwPz3LbeUe zfUZc/EJodjeeuSsbTp3Ucu2ZeuBHV7n+SrrrvQMnj95EWmBJiBYks9fUB1Isc+cOyYU eJQcvnE3pFCtuGPHfklvb2Yz6VhF6eH3O2v6qfPszvEcxogIs9T7KT3qpOZKUsOwJZSa X4b55jE+7Jz5PlRhhIb2+ov9MeNCOkJycYRx8dVHcS+KVjINR76jEUUtAEIWH15zEgYL Secg== X-Forwarded-Encrypted: i=1; AJvYcCWPSqjXWZn2RMsph8vpBvX07gDGFH8btbkRACx9U6zMclO9FNwQ8TmaH3owqEhiFQK7DGora8sCAaZI7Gc=@vger.kernel.org X-Gm-Message-State: AOJu0YwpRN9HQcZ/8R7jkw/bfQqh0Q6JU50UFS8vTMY/uEnk0UmOnr52 VGxKb1G2NA4nJxes6opwzAkarHWJE8SmtjryDVa3PgaRpu43H3LF8NmJ6hyg/n2PbMYaNf6OyDq fGbbOOhLd6VMA1O9xNfRRqZNtt2Q2qNpRaN+f8EbKwbOaKwDj/C5XQ3FUze456psB8vcbCFQyJv 2MmA== X-Gm-Gg: ASbGncvaOQ6rNYid+Ldmxh4uLL/XAghwuSkhmgM/HJpJ1td4QuWqALddoGvIwP6BS9e H3Zh8Uu2Uhpz3QSGDKXnL2xFZZRPksKD2fJ+wQzprfdpdC6B/uDKpgSArnFWO0CgjGTlGgqNtMK YqGfdf9svpc5v8pMbQOQ51xuOgSX6ouIYeew/n2IBkwmJ1iSXDe2gIxw1vGMtFNlRhAnBrsRBqL SDW63rMvVWci0p9Bde9WR2z8phx/S0l/571qJzbL648mesEKzzZO/8VPHI0SqzdvI94p57yW39a 17Vu3Zza6B4ug6bN X-Received: by 2002:a05:6a00:2386:b0:72f:590f:2859 with SMTP id d2e1a72fcca58-7372239bc8amr7223069b3a.13.1742056902409; Sat, 15 Mar 2025 09:41:42 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFeq/kvkK0jNLUi9P+uI5WAcpgGTALHCSgvR27k0PL9TBtkJ0a7+DJ5Y8rQ4HPuiZm5VxzX+A== X-Received: by 2002:a05:6a00:2386:b0:72f:590f:2859 with SMTP id d2e1a72fcca58-7372239bc8amr7223047b3a.13.1742056901939; Sat, 15 Mar 2025 09:41:41 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:41 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 4/9] gpio: aggregator: introduce basic configfs interface Date: Sun, 16 Mar 2025 01:41:18 +0900 Message-ID: <20250315164123.1855142-5-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The existing sysfs 'new_device' interface has several limitations: * No way to determine when GPIO aggregator creation is complete. * No way to retrieve errors when creating a GPIO aggregator. * No way to trace a GPIO line of an aggregator back to its corresponding physical device. * The 'new_device' echo does not indicate which virtual gpiochip was created. * No way to assign names to GPIO lines exported through an aggregator. Introduce the new configfs interface for gpio-aggregator to address these limitations. It provides a more streamlined, modern, and extensible configuration method. For backward compatibility, the 'new_device' interface and its behavior is retained for now. This commit implements basic functionalities: /config/gpio-aggregator// /config/gpio-aggregator//live /config/gpio-aggregator//dev_name /config/gpio-aggregator/// /config/gpio-aggregator///key /config/gpio-aggregator///offset /config/gpio-aggregator///name Basic setup flow is: 1. Create a directory for a GPIO aggregator. 2. Create subdirectories for each line you want to instantiate. 3. In each line directory, configure the key and offset. The key/offset semantics are as follows: * If offset is >=3D 0: - key specifies the name of the chip this GPIO belongs to - offset specifies the line offset within that chip. * If offset is <0: - key needs to specify the GPIO line name. 4. Return to the aggregator's root directory and write '1' to the live attribute. For example, the command in the existing kernel doc: echo 'e6052000.gpio 19 e6050000.gpio 20-21' > new_device is equivalent to: mkdir /sys/kernel/config/gpio-aggregator/ # Change to name of your choice (e.g. "aggr0") cd /sys/kernel/config/gpio-aggregator/ mkdir line0 line1 line2 # Only "line" naming allowed. echo e6052000.gpio > line0/key echo 19 > line0/offset echo e6050000.gpio > line1/key echo 20 > line1/offset echo e6050000.gpio > line2/key echo 21 > line2/offset echo 1 > live The corresponding gpio_device id can be identified as follows: cd /sys/kernel/config/gpio-aggregator/ ls -d /sys/devices/platform/`cat dev_name`/gpiochip* Also, via configfs, custom GPIO line name can be set like this: cd /sys/kernel/config/gpio-aggregator/ echo "abc" > line1/name Signed-off-by: Koichiro Den --- drivers/gpio/Kconfig | 2 + drivers/gpio/gpio-aggregator.c | 636 ++++++++++++++++++++++++++++++++- 2 files changed, 626 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3e9b174fee84..5eae5606dcb9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1869,6 +1869,8 @@ menu "Virtual GPIO drivers" =20 config GPIO_AGGREGATOR tristate "GPIO Aggregator" + select CONFIGFS_FS + select DEV_SYNC_PROBE help Say yes here to enable the GPIO Aggregator, which provides a way to aggregate existing GPIO lines into a new virtual GPIO chip. diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 2692a31e01ac..0afd5d43812b 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -9,10 +9,13 @@ =20 #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,8 @@ #include #include =20 +#include "dev-sync-probe.h" + #define AGGREGATOR_MAX_GPIOS 512 =20 /* @@ -34,12 +39,38 @@ */ =20 struct gpio_aggregator { + struct dev_sync_probe_data probe_data; + struct config_group group; struct gpiod_lookup_table *lookups; - struct platform_device *pdev; + struct mutex lock; int id; + + /* List of gpio_aggregator_line. Always added in order */ + struct list_head list_head; + + /* used by legacy sysfs interface only */ + bool init_via_sysfs; char args[]; }; =20 +struct gpio_aggregator_line { + struct config_group group; + struct gpio_aggregator *parent; + struct list_head entry; + + /* Line index within the aggregator device */ + unsigned int idx; + + /* Custom name for the virtual line */ + const char *name; + /* GPIO chip label or line name */ + const char *key; + /* Can be negative to indicate lookup by line name */ + int offset; + + enum gpio_lookup_flags flags; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 @@ -59,6 +90,8 @@ static int aggr_alloc(struct gpio_aggregator **aggr, size= _t arg_size) return ret; =20 new->id =3D ret; + INIT_LIST_HEAD(&new->list_head); + mutex_init(&new->lock); *aggr =3D no_free_ptr(new); return 0; } @@ -68,6 +101,7 @@ static void aggr_free(struct gpio_aggregator *aggr) mutex_lock(&gpio_aggregator_lock); idr_remove(&gpio_aggregator_idr, aggr->id); mutex_unlock(&gpio_aggregator_lock); + mutex_destroy(&aggr->lock); kfree(aggr); } =20 @@ -90,6 +124,70 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, = const char *key, return 0; } =20 +static bool aggr_is_active(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pde= v); +} + +static size_t aggr_count_lines(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return list_count_nodes(&aggr->list_head); +} + +static struct gpio_aggregator_line *aggr_line_alloc( + struct gpio_aggregator *parent, unsigned int idx, char *key, int offset) +{ + struct gpio_aggregator_line *line; + + line =3D kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return ERR_PTR(-ENOMEM); + + if (key) { + line->key =3D kstrdup(key, GFP_KERNEL); + if (!line->key) { + kfree(line); + return ERR_PTR(-ENOMEM); + } + } + + line->flags =3D GPIO_LOOKUP_FLAGS_DEFAULT; + line->parent =3D parent; + line->idx =3D idx; + line->offset =3D offset; + INIT_LIST_HEAD(&line->entry); + + return line; +} + +static void aggr_line_add(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) +{ + struct gpio_aggregator_line *tmp; + + lockdep_assert_held(&aggr->lock); + + list_for_each_entry(tmp, &aggr->list_head, entry) { + if (tmp->idx > line->idx) { + list_add_tail(&line->entry, &tmp->entry); + return; + } + } + list_add_tail(&line->entry, &aggr->list_head); +} + +static void aggr_line_del(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) +{ + lockdep_assert_held(&aggr->lock); + + list_del(&line->entry); +} + =20 /* * GPIO Forwarder @@ -424,6 +522,474 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struc= t device *dev, } =20 =20 +/* + * Configfs interface + */ + +static struct gpio_aggregator * +to_gpio_aggregator(struct config_item *item) +{ + struct config_group *group =3D to_config_group(item); + + return container_of(group, struct gpio_aggregator, group); +} + +static struct gpio_aggregator_line * +to_gpio_aggregator_line(struct config_item *item) +{ + struct config_group *group =3D to_config_group(item); + + return container_of(group, struct gpio_aggregator_line, group); +} + +static struct fwnode_handle *aggr_make_device_swnode(struct gpio_aggregato= r *aggr) +{ + const char **line_names __free(kfree) =3D NULL; + struct property_entry properties[2]; + struct gpio_aggregator_line *line; + size_t num_lines; + int n =3D 0; + + memset(properties, 0, sizeof(properties)); + + num_lines =3D aggr_count_lines(aggr); + if (num_lines =3D=3D 0) + return NULL; + + line_names =3D kcalloc(num_lines, sizeof(*line_names), GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) + line_names[n++] =3D line->name ?: ""; + + properties[0] =3D PROPERTY_ENTRY_STRING_ARRAY_LEN( + "gpio-line-names", + line_names, num_lines); + + return fwnode_create_software_node(properties, NULL); +} + +static int aggr_activate(struct gpio_aggregator *aggr) +{ + struct platform_device_info pdevinfo; + struct gpio_aggregator_line *line; + struct fwnode_handle *swnode; + unsigned int n =3D 0; + int ret =3D 0; + + if (aggr_count_lines(aggr) =3D=3D 0) + return -EINVAL; + + aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) + return -ENOMEM; + + swnode =3D aggr_make_device_swnode(aggr); + if (IS_ERR(swnode)) + goto err_remove_lookups; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name =3D DRV_NAME; + pdevinfo.id =3D aggr->id; + pdevinfo.fwnode =3D swnode; + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) { + /* + * - Either GPIO chip label or line name must be configured + * (i.e. line->key must be non-NULL) + * - Line directories must be named with sequential numeric + * suffixes starting from 0. (i.e. ./line0, ./line1, ...) + */ + if (!line->key || line->idx !=3D n) { + ret =3D -EINVAL; + goto err_remove_swnode; + } + + if (line->offset < 0) + ret =3D aggr_add_gpio(aggr, line->key, U16_MAX, &n); + else + ret =3D aggr_add_gpio(aggr, line->key, line->offset, &n); + if (ret) + goto err_remove_swnode; + } + + aggr->lookups->dev_id =3D kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->= id); + if (!aggr->lookups->dev_id) { + ret =3D -ENOMEM; + goto err_remove_swnode; + } + + gpiod_add_lookup_table(aggr->lookups); + + ret =3D dev_sync_probe_register(&aggr->probe_data, &pdevinfo); + if (ret) + goto err_remove_lookup_table; + + return 0; + +err_remove_lookup_table: + kfree(aggr->lookups->dev_id); + gpiod_remove_lookup_table(aggr->lookups); +err_remove_swnode: + fwnode_remove_software_node(swnode); +err_remove_lookups: + kfree(aggr->lookups); + + return ret; +} + +static void aggr_deactivate(struct gpio_aggregator *aggr) +{ + dev_sync_probe_unregister(&aggr->probe_data); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); +} + +static void aggr_lockup_configfs(struct gpio_aggregator *aggr, bool lock) +{ + struct configfs_subsystem *subsys =3D aggr->group.cg_subsys; + struct gpio_aggregator_line *line; + + /* + * The device only needs to depend on leaf lines. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(line, &aggr->list_head, entry) { + if (lock) + configfs_depend_item_unlocked( + subsys, &line->group.cg_item); + else + configfs_undepend_item_unlocked( + &line->group.cg_item); + } +} + +static ssize_t +gpio_aggr_line_key_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->key ?: ""); +} + +static ssize_t +gpio_aggr_line_key_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + char *key __free(kfree) =3D kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!key) + return -ENOMEM; + + strim(key); + + guard(mutex)(&aggr->lock); + + if (aggr_is_active(aggr)) + return -EBUSY; + + kfree(line->key); + line->key =3D no_free_ptr(key); + + return count; +} +CONFIGFS_ATTR(gpio_aggr_line_, key); + +static ssize_t +gpio_aggr_line_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->name ?: ""); +} + +static ssize_t +gpio_aggr_line_name_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + char *name __free(kfree) =3D kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!name) + return -ENOMEM; + + strim(name); + + guard(mutex)(&aggr->lock); + + if (aggr_is_active(aggr)) + return -EBUSY; + + kfree(line->name); + line->name =3D no_free_ptr(name); + + return count; +} +CONFIGFS_ATTR(gpio_aggr_line_, name); + +static ssize_t +gpio_aggr_line_offset_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + unsigned int offset; + + scoped_guard(mutex, &aggr->lock) + offset =3D line->offset; + + return sysfs_emit(page, "%d\n", offset); +} + +static ssize_t +gpio_aggr_line_offset_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + int offset, ret; + + ret =3D kstrtoint(page, 0, &offset); + if (ret) + return ret; + + /* + * When offset =3D=3D -1, 'key' represents a line name to lookup. + * When 0 <=3D offset < 65535, 'key' represents the label of the chip with + * the 'offset' value representing the line within that chip. + * + * GPIOLIB uses the U16_MAX value to indicate lookup by line name so + * the greatest offset we can accept is (U16_MAX - 1). + */ + if (offset > (U16_MAX - 1) || offset < -1) + return -EINVAL; + + guard(mutex)(&aggr->lock); + + if (aggr_is_active(aggr)) + return -EBUSY; + + line->offset =3D offset; + + return count; +} +CONFIGFS_ATTR(gpio_aggr_line_, offset); + +static struct configfs_attribute *gpio_aggr_line_attrs[] =3D { + &gpio_aggr_line_attr_key, + &gpio_aggr_line_attr_name, + &gpio_aggr_line_attr_offset, + NULL +}; + +static ssize_t +gpio_aggr_device_dev_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + struct platform_device *pdev; + + guard(mutex)(&aggr->lock); + + pdev =3D aggr->probe_data.pdev; + if (pdev) + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + + return sysfs_emit(page, "%s.%d\n", DRV_NAME, aggr->id); +} +CONFIGFS_ATTR_RO(gpio_aggr_device_, dev_name); + +static ssize_t +gpio_aggr_device_live_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + bool active; + + scoped_guard(mutex, &aggr->lock) + active =3D aggr_is_active(aggr); + + return sysfs_emit(page, "%c\n", active ? '1' : '0'); +} + +static ssize_t +gpio_aggr_device_live_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + int ret =3D 0; + bool live; + + ret =3D kstrtobool(page, &live); + if (ret) + return ret; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + if (live) + aggr_lockup_configfs(aggr, true); + + scoped_guard(mutex, &aggr->lock) { + if (live =3D=3D aggr_is_active(aggr)) + ret =3D -EPERM; + else if (live) + ret =3D aggr_activate(aggr); + else + aggr_deactivate(aggr); + } + + /* + * Undepend is required only if device disablement (live =3D=3D 0) + * succeeds or if device enablement (live =3D=3D 1) fails. + */ + if (live =3D=3D !!ret) + aggr_lockup_configfs(aggr, false); + + module_put(THIS_MODULE); + + return ret ?: count; +} +CONFIGFS_ATTR(gpio_aggr_device_, live); + +static struct configfs_attribute *gpio_aggr_device_attrs[] =3D { + &gpio_aggr_device_attr_dev_name, + &gpio_aggr_device_attr_live, + NULL +}; + +static void +gpio_aggr_line_release(struct config_item *item) +{ + struct gpio_aggregator_line *line =3D to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr =3D line->parent; + + guard(mutex)(&aggr->lock); + + aggr_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); +} + +static struct configfs_item_operations gpio_aggr_line_item_ops =3D { + .release =3D gpio_aggr_line_release, +}; + +static const struct config_item_type gpio_aggr_line_type =3D { + .ct_item_ops =3D &gpio_aggr_line_item_ops, + .ct_attrs =3D gpio_aggr_line_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static void gpio_aggr_device_release(struct config_item *item) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); + + /* + * If the aggregator is active, this code wouldn't be reached, + * so calling aggr_deactivate() is always unnecessary. + */ + aggr_free(aggr); +} + +static struct configfs_item_operations gpio_aggr_device_item_ops =3D { + .release =3D gpio_aggr_device_release, +}; + +static struct config_group * +gpio_aggr_device_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr =3D to_gpio_aggregator(&group->cg_item); + struct gpio_aggregator_line *line; + unsigned int idx; + int ret, nchar; + + ret =3D sscanf(name, "line%u%n", &idx, &nchar); + if (ret !=3D 1 || nchar !=3D strlen(name)) + return ERR_PTR(-EINVAL); + + guard(mutex)(&aggr->lock); + + if (aggr_is_active(aggr)) + return ERR_PTR(-EBUSY); + + list_for_each_entry(line, &aggr->list_head, entry) + if (line->idx =3D=3D idx) + return ERR_PTR(-EINVAL); + + line =3D aggr_line_alloc(aggr, idx, NULL, -1); + if (!line) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&line->group, name, &gpio_aggr_line_type); + + aggr_line_add(aggr, line); + + return &line->group; +} + +static struct configfs_group_operations gpio_aggr_device_group_ops =3D { + .make_group =3D gpio_aggr_device_make_group, +}; + +static const struct config_item_type gpio_aggr_device_type =3D { + .ct_group_ops =3D &gpio_aggr_device_group_ops, + .ct_item_ops =3D &gpio_aggr_device_item_ops, + .ct_attrs =3D gpio_aggr_device_attrs, + .ct_owner =3D THIS_MODULE, +}; + +static struct config_group * +gpio_aggr_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr; + int ret; + + /* arg space is unneeded */ + ret =3D aggr_alloc(&aggr, 0); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggr_device_type); + dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; +} + +static struct configfs_group_operations gpio_aggr_group_ops =3D { + .make_group =3D gpio_aggr_make_group, +}; + +static const struct config_item_type gpio_aggr_type =3D { + .ct_group_ops =3D &gpio_aggr_group_ops, + .ct_owner =3D THIS_MODULE, +}; + +static struct configfs_subsystem gpio_aggr_subsys =3D { + .su_group =3D { + .cg_item =3D { + .ci_namebuf =3D DRV_NAME, + .ci_type =3D &gpio_aggr_type, + }, + }, +}; + + /* * Sysfs interface */ @@ -495,6 +1061,7 @@ static ssize_t new_device_store(struct device_driver *= driver, const char *buf, =20 memcpy(aggr->args, buf, count + 1); =20 + aggr->init_via_sysfs =3D true; aggr->lookups =3D kzalloc(struct_size(aggr->lookups, table, 1), GFP_KERNEL); if (!aggr->lookups) { @@ -520,7 +1087,7 @@ static ssize_t new_device_store(struct device_driver *= driver, const char *buf, goto remove_table; } =20 - aggr->pdev =3D pdev; + aggr->probe_data.pdev =3D pdev; module_put(THIS_MODULE); return count; =20 @@ -541,10 +1108,7 @@ static DRIVER_ATTR_WO(new_device); =20 static void gpio_aggregator_free(struct gpio_aggregator *aggr) { - platform_device_unregister(aggr->pdev); - gpiod_remove_lookup_table(aggr->lookups); - kfree(aggr->lookups->dev_id); - kfree(aggr->lookups); + aggr_deactivate(aggr); kfree(aggr); } =20 @@ -566,12 +1130,19 @@ static ssize_t delete_device_store(struct device_dri= ver *driver, return -ENOENT; =20 mutex_lock(&gpio_aggregator_lock); - aggr =3D idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { + aggr =3D idr_find(&gpio_aggregator_idr, id); + /* + * For simplicity, devices created via configfs cannot be deleted + * via sysfs. + */ + if (aggr && aggr->init_via_sysfs) + idr_remove(&gpio_aggregator_idr, id); + else { + mutex_unlock(&gpio_aggregator_lock); module_put(THIS_MODULE); return -ENOENT; } + mutex_unlock(&gpio_aggregator_lock); =20 gpio_aggregator_free(aggr); module_put(THIS_MODULE); @@ -646,21 +1217,61 @@ static struct platform_driver gpio_aggregator_driver= =3D { =20 static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) { + /* + * There should be no aggregator created via configfs, as their + * presence would prevent module unloading. + */ gpio_aggregator_free(p); return 0; } =20 static void __exit gpio_aggregator_remove_all(void) { - mutex_lock(&gpio_aggregator_lock); + /* + * Configfs callbacks acquire gpio_aggregator_lock when accessing + * gpio_aggregator_idr, so to prevent lock inversion deadlock, we + * cannot protect idr_for_each invocation here with + * gpio_aggregator_lock, as gpio_aggregator_idr_remove() accesses + * configfs groups. Fortunately, the new_device/delete_device path + * and the module unload path are mutually exclusive, thanks to an + * explicit try_module_get inside of those driver attr handlers. + * Also, when we reach here, no configfs entries present or being + * created. Therefore, no need to protect with gpio_aggregator_lock + * below. + */ idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); idr_destroy(&gpio_aggregator_idr); - mutex_unlock(&gpio_aggregator_lock); } =20 static int __init gpio_aggregator_init(void) { - return platform_driver_register(&gpio_aggregator_driver); + int ret =3D 0; + + config_group_init(&gpio_aggr_subsys.su_group); + mutex_init(&gpio_aggr_subsys.su_mutex); + ret =3D configfs_register_subsystem(&gpio_aggr_subsys); + if (ret) { + pr_err("Failed to register the '%s' configfs subsystem: %d\n", + gpio_aggr_subsys.su_group.cg_item.ci_namebuf, ret); + mutex_destroy(&gpio_aggr_subsys.su_mutex); + return ret; + } + + /* + * CAVEAT: This must occur after configfs registration. Otherwise, + * a race condition could arise: driver attribute groups might be + * exposed and accessed by users before configfs registration + * completes. new_device_store() does not expect a partially + * initialized configfs state. + */ + ret =3D platform_driver_register(&gpio_aggregator_driver); + if (ret) { + pr_err("Failed to register the platform driver: %d\n", ret); + mutex_destroy(&gpio_aggr_subsys.su_mutex); + configfs_unregister_subsystem(&gpio_aggr_subsys); + } + + return ret; } module_init(gpio_aggregator_init); =20 @@ -668,6 +1279,7 @@ static void __exit gpio_aggregator_exit(void) { gpio_aggregator_remove_all(); platform_driver_unregister(&gpio_aggregator_driver); + configfs_unregister_subsystem(&gpio_aggr_subsys); } module_exit(gpio_aggregator_exit); =20 --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6B76204F8B for ; Sat, 15 Mar 2025 16:41:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056910; cv=none; b=AV9C+mOFzoyhIhYr3j4xs/MRU6zaJQnP1iERGjxD4D1FqdI78ZeYGLCW5oW19P3AJ29Fp7KB6kBx5PQqqwBQ8AG6qlHW8zoJlJpAeDUAlAAjSVrzqePB2lPqkVLpROl0AQ+M/TDb+waSZoJE8JQ2gh1+fAjJVwusLGbFGaiL3oE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056910; c=relaxed/simple; bh=hcT6Pkv849JRDuuXpPNqMLlmqrx2ytCXKRtqnB9DgRo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c4civf4miskopIN0W8YXoB2hiJVEBu06yN5UiuSA4awSdGf4oloRknNpON1MnMiR9MxSbltPMi4o6a3AQjMQG6bVnV975sUlgI/HhZC4r1UkeqbHlnxjMnsB4FcjlfdldoJoV+EwFbXcprKKwJeUsFuzq7jAgljJ8Fp2ZV+vvgs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=jPqaJHDs; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="jPqaJHDs" Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id E248D3F2B1 for ; Sat, 15 Mar 2025 16:41:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056905; bh=6y2HDbZhaY8Idh2slBQTtYTVvrytnAsA4PhON2tVLMA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jPqaJHDsXozzuKhog9ZiQrFwFHjwvQu8k+1ZYjKMSlX+sihSciq1GiMm17No4rufv hxampk4Lt1WV78y+ZC93CUGrmnGVQOu3tRBCLdhkdfxqORhPps1W/Bpy3D0PwGY4G0 HIDg6RBomnXO24wn9zMvM999pwvauIl879CdR2O4GzKjGDU6jebHGnREvIgbOR4bMC M5OLeMz2pdBsS+40tBnXgOipSONHVtwQdq3cCSMs4djst72iC0A2GXUzUUAb51Gkkm 87PDpuWplz7VaB4/MGpRQZ585WGTvAdB2Q4vQqteh7Ghoky0cak3eLU3kFxWex3b0r gL1KXIDWqONhw== Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2240c997059so68918585ad.0 for ; Sat, 15 Mar 2025 09:41:45 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056904; x=1742661704; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6y2HDbZhaY8Idh2slBQTtYTVvrytnAsA4PhON2tVLMA=; b=MDvMdzl5z6Vizx7cpGXECibOhJ8+TeraN4TAo/3nNKIXs5+Mg47MpgmZMbF/To+D0H ePmW31ME39H7vrFfUgpCZHxmoekFOCXYwu8z0EZPIh7boXPVdQst4XNK9CER1GsvYYlQ qn2RCvt4uItGyXtEhcYV52oThwJY+VvUn04O9ouv8CA7OzeiUuYBNKvxMGRTmWUOhoLz V3DosDzIl0UHn9W4rx2/+jQpg4IZCbALzzYmzV/GNMtlGGKeQb+wKzWk2dkjccgWRZ2H wgkMkUyJCvCUuJUhtNE7VGzny8xn+nt2lJyJJ0eyyiDbR6iOTCi9T5+q5/NsPPkm1kfp crrg== X-Forwarded-Encrypted: i=1; AJvYcCULkAGxAQywLuBnsPQFfcT4121khjvHq3U8qMrFJtF1NKiU5sXwhVHvR4mFHk8caJey/hexa+A4h1WR3q0=@vger.kernel.org X-Gm-Message-State: AOJu0YwXtvar5FEcXjLlQNXWGus8O3zlv3/IZBjj5a3ilr2EC3xDMb2h DdEHLlZyiXzHuaV5V4hgdmKNQNwzmFfOPl705MZeWiiD92BJ5yEHKaxxXM1EgCeGEKpdVfMrVqf iI9zTNApeKrW3wiAJHk8izRGJ154pESwknxwCWR1YSbNcmnbycmokjt2fsPs2XA0G3BSGXtfJ6W 1Jyg== X-Gm-Gg: ASbGncsNVEWFb4HzG/kjBuP57xs2Kh1qwZdOxy+vOG+yNzl2RU4vA9RohLh471Y8yrh rf0T3SarxD0mb5NrF96Eeo8F6AllnjebI6cpY1juY4rF//qaEypd2PFSQpasRF8RlocoaBkcnyB KXcC204pHyhsUdz/3ObSpJV0tZDjCtgC+h5o5w1+NIoGhZzctLjQOAMrP+8u8jENw871PVQDKTl wUJLdwVEnSGn3+kTvMclGHrrhn7S/j2bF0K7FtqcqtV+VACxJ7NmDC062oI3cAoxcNp150IugYh WLyZiCD6sw3TZne5 X-Received: by 2002:a17:902:c411:b0:220:ff3f:6cba with SMTP id d9443c01a7336-225e0ae70b8mr74365865ad.38.1742056904600; Sat, 15 Mar 2025 09:41:44 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFMXBdPfzXEyy/Oz7vzl5WYmItCelN4wOI+AARmFHRLdkSNEbkkAQGzuJPH0K7+rDBLiTC1sg== X-Received: by 2002:a17:902:c411:b0:220:ff3f:6cba with SMTP id d9443c01a7336-225e0ae70b8mr74365755ad.38.1742056904249; Sat, 15 Mar 2025 09:41:44 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:43 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 5/9] gpio: aggregator: rename 'name' to 'key' in aggr_parse() Date: Sun, 16 Mar 2025 01:41:19 +0900 Message-ID: <20250315164123.1855142-6-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Rename the local variable 'name' in aggr_parse() to 'key' because struct gpio_aggregator_line now uses the 'name' field for the custom line name and the local variable actually represents a 'key'. This change prepares for the next but one commit. No functional change. Reviewed-by: Geert Uytterhoeven Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 0afd5d43812b..7087ac599ace 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -996,7 +996,7 @@ static struct configfs_subsystem gpio_aggr_subsys =3D { static int aggr_parse(struct gpio_aggregator *aggr) { char *args =3D skip_spaces(aggr->args); - char *name, *offsets, *p; + char *key, *offsets, *p; unsigned int i, n =3D 0; int error =3D 0; =20 @@ -1005,18 +1005,18 @@ static int aggr_parse(struct gpio_aggregator *aggr) if (!bitmap) return -ENOMEM; =20 - args =3D next_arg(args, &name, &p); + args =3D next_arg(args, &key, &p); while (*args) { args =3D next_arg(args, &offsets, &p); =20 p =3D get_options(offsets, 0, &error); if (error =3D=3D 0 || *p) { /* Named GPIO line */ - error =3D aggr_add_gpio(aggr, name, U16_MAX, &n); + error =3D aggr_add_gpio(aggr, key, U16_MAX, &n); if (error) return error; =20 - name =3D offsets; + key =3D offsets; continue; } =20 @@ -1028,12 +1028,12 @@ static int aggr_parse(struct gpio_aggregator *aggr) } =20 for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error =3D aggr_add_gpio(aggr, name, i, &n); + error =3D aggr_add_gpio(aggr, key, i, &n); if (error) return error; } =20 - args =3D next_arg(args, &name, &p); + args =3D next_arg(args, &key, &p); } =20 if (!n) { --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6FF4CD26D for ; Sat, 15 Mar 2025 16:41:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056913; cv=none; b=adOgAtgeXxgnCJ4Xk8MIUGTpsapFjORqC7Ej5wa5M+GW1pQ1hnafMY5h/rDRU7Gv6r3u99R6l+AHMtwH+KBNe5xmrJqgNR01dtxOMczvgFmkvfe0N/oFvNH5Xz8oGNR7wSJBbxCVDw2pdp8l39ipK7DFKYavf13guobAMlAbrxw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056913; c=relaxed/simple; bh=tZpIixhIz63xwZmTsIFO5W2unoS3No4bx4rWhDwxvBw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kS3Jzbgz/EoZSN2KxhRYK10bluLnJ8NakKu3HegL0BdiaWfh0Npb4uCgEujT/eLqhRebl3iMqpkOASnIhKVJ9ozHNe6PDjBihsNc9djNyQ+KAQEyy2uYISEgtkenf5SkGfzrY81092xbKqC1UrkVh3zXv0nr3MOEQGJUz6IwEbU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=QZdKtUgJ; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="QZdKtUgJ" Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 0C6C73F31D for ; Sat, 15 Mar 2025 16:41:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056909; bh=YNZ+f28bALe1cBFd+IhRUCCUv+emwMU47KrwcCRAyIY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QZdKtUgJuJ1boOzOiSSDTIBqCv1jFD1NQjgdazMUSNM7JAkqHtbANXWt7L+Kzm1js 5jALZchaJUczi64plAv16CCuTdWR6qZslJrJMM3DlcFeagCUBhsVhNCWx/FskCn275 yGFHTrtbrmFMB8eP6VUzVbfOcaB1M2IbGvmOv5ofzuHVjHixCMnhRDPp6HTuZzNjqt v0tYeEMxhexOm2joEJhi8zOQQEoymPe9PCUrzhVDfT44ajAzF8uw2/sVebpd/xjS2p J5HQ+952oXn8+S6jXTrbOK5QRqW80S8JPKuPniTBvtiqspqpwXpksGs2VzRpGvZw6N EpZlXhMNLgonw== Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-2ff5296726fso2044391a91.0 for ; Sat, 15 Mar 2025 09:41:48 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056907; x=1742661707; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YNZ+f28bALe1cBFd+IhRUCCUv+emwMU47KrwcCRAyIY=; b=Jg7UfOIdqeOvpz9VT1WCAKnnA5m19cmbPwSj4gNmyndbAFNgh0cpFmf2bTu02W6X2P PVv9+LbXQJJj0HtGHWWxEbXLLcy7vuSGIHnRVLHKMzu/XDmHwqhZImWBc/LUUIPFEeez w+jpW/X9sYFCcZko/censK5lEW1z6tAQNbzbNmH0MLrlsWD4dEOcnujaMsj5FNBaJcQG 3xPpMam5RbV8d3CuWONc/1mVIxFwtzcrTBeBtMDJ9Q5Ej7DBcZG1cp7IJXj1wP0NW1Sx 79IiieHx/axwScaSWwZEZwCNad21j8CJMWxl72wlJ0aJn/nPnhY0fkCx6wI00PK7Q4gK 4M9g== X-Forwarded-Encrypted: i=1; AJvYcCW3P/UktLUG01v4M6Rrqpuh7+2+wpxUcJty/x+jPCs7umfxc5jXjJZiRKMPjVg3q7PXwlG4bDzvMw7NTJE=@vger.kernel.org X-Gm-Message-State: AOJu0YxS/yOKw/MaL8YEGL2CEWaK61amwDuSPUa3iDL7pgtZJkBJmFie n89PJ5iLvWXmg7VXz02eD6Dd117Vff8VWLFrFP2JsDM9MQVTPLtO1VWwN2AyazDhLichyyED/p9 pbbohS9+kmkx3WjdKdh+geUiNa5SWKdhfAAVGW2ze36zaLzylfLRHRWPCupOHmKEiI0R5Jp+jzF Jx/Q== X-Gm-Gg: ASbGncsvo9UzoQW6Av5i7YdRQ6bNvajc1vQ0hLLA0i/9VIdMF7m3BRbSvTLC20yBL9Q uf3OqwsBLbykxKiRD6HoNDuUJ3PuMpDHmnYMltIcxGBUoAi6u3prf7ypEK3EZl5sk7LC0dJpcQr cByCByK1Z58CW0pnRmqaIP/y7cpMLejZ1o12dKT73chM+uZt35G6+s8iXOrCLf/fLn/6dWacO3c bI9esnOnRJNJGx+vE3110/YxArzDXXWNOxxUDsGTRBYHFLkxXiFJZj5nJPyXAs4vlYY3BQMBZXY VA2Q9cW/N4sf+nxU X-Received: by 2002:a05:6a21:9014:b0:1f5:87dc:a315 with SMTP id adf61e73a8af0-1f5c1183d5emr9009306637.12.1742056907219; Sat, 15 Mar 2025 09:41:47 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGU0ceoD51BZ11smOJZX/JIzzQoRljOZk4JbL6veMWIaCrSH6Aif/TUrWT0CUiSbp4awP4Kbg== X-Received: by 2002:a05:6a21:9014:b0:1f5:87dc:a315 with SMTP id adf61e73a8af0-1f5c1183d5emr9009286637.12.1742056906854; Sat, 15 Mar 2025 09:41:46 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:46 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 6/9] gpio: aggregator: expose aggregator created via legacy sysfs to configfs Date: Sun, 16 Mar 2025 01:41:20 +0900 Message-ID: <20250315164123.1855142-7-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose settings for aggregators created using the sysfs 'new_device' interface to configfs. Once written to 'new_device', an "_sysfs." path appears in the configfs regardless of whether the probe succeeds. Consequently, users can no longer use that prefix for custom GPIO aggregator names. The 'live' attribute changes to 1 when the probe succeeds and the GPIO forwarder is instantiated. Note that the aggregator device created via sysfs is asynchronous, i.e. writing into 'new_device' returns without waiting for probe completion, and the probe may succeed, fail, or eventually succeed via deferred probe. Thus, the 'live' attribute may change from 0 to 1 asynchronously without notice. So, editing key/offset/name while it's waiting for deferred probe is prohibited. The configfs auto-generation relies on create_default_group(), which inherently prohibits rmdir(2). To align with the limitation, this commit also prohibits mkdir(2) for them. When users want to change the number of lines for an aggregator initialized via 'new_device', they need to tear down the device using 'delete_device' and reconfigure it from scratch. This does not break previous behavior; users of legacy sysfs interface simply gain additional almost read-only configfs exposure. Still, users can write to the 'live' attribute to toggle the device unless it's waiting for deferred probe. So once probe succeeds, they can deactivate it in the same manner as the devices initialized via configfs. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 133 ++++++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 7087ac599ace..de693182070c 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -33,6 +33,7 @@ #include "dev-sync-probe.h" =20 #define AGGREGATOR_MAX_GPIOS 512 +#define AGGREGATOR_LEGACY_PREFIX "_sysfs" =20 /* * GPIO Aggregator sysfs interface @@ -131,6 +132,14 @@ static bool aggr_is_active(struct gpio_aggregator *agg= r) return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pde= v); } =20 +/* Only aggregators created via legacy sysfs can be "activating". */ +static bool aggr_is_activating(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); + + return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pd= ev); +} + static size_t aggr_count_lines(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); @@ -188,6 +197,30 @@ static void aggr_line_del(struct gpio_aggregator *aggr, list_del(&line->entry); } =20 +static void aggr_free_lines(struct gpio_aggregator *aggr) +{ + struct gpio_aggregator_line *line, *tmp; + + list_for_each_entry_safe(line, tmp, &aggr->list_head, entry) { + configfs_unregister_group(&line->group); + /* + * Normally, we acquire aggr->lock within the configfs + * callback. However, in the legacy sysfs interface case, + * calling configfs_(un)register_group while holding + * aggr->lock could cause a deadlock. Fortunately, this is + * unnecessary because the new_device/delete_device path + * and the module unload path are mutually exclusive, + * thanks to an explicit try_module_get. That's why this + * minimal scoped_guard suffices. + */ + scoped_guard(mutex, &aggr->lock) + aggr_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); + } +} + =20 /* * GPIO Forwarder @@ -697,7 +730,7 @@ gpio_aggr_line_key_store(struct config_item *item, cons= t char *page, =20 guard(mutex)(&aggr->lock); =20 - if (aggr_is_active(aggr)) + if (aggr_is_activating(aggr) || aggr_is_active(aggr)) return -EBUSY; =20 kfree(line->key); @@ -734,7 +767,7 @@ gpio_aggr_line_name_store(struct config_item *item, con= st char *page, =20 guard(mutex)(&aggr->lock); =20 - if (aggr_is_active(aggr)) + if (aggr_is_activating(aggr) || aggr_is_active(aggr)) return -EBUSY; =20 kfree(line->name); @@ -782,7 +815,7 @@ gpio_aggr_line_offset_store(struct config_item *item, c= onst char *page, =20 guard(mutex)(&aggr->lock); =20 - if (aggr_is_active(aggr)) + if (aggr_is_activating(aggr) || aggr_is_active(aggr)) return -EBUSY; =20 line->offset =3D offset; @@ -841,11 +874,12 @@ gpio_aggr_device_live_store(struct config_item *item,= const char *page, if (!try_module_get(THIS_MODULE)) return -ENOENT; =20 - if (live) + if (live && !aggr->init_via_sysfs) aggr_lockup_configfs(aggr, true); =20 scoped_guard(mutex, &aggr->lock) { - if (live =3D=3D aggr_is_active(aggr)) + if (aggr_is_activating(aggr) || + (live =3D=3D aggr_is_active(aggr))) ret =3D -EPERM; else if (live) ret =3D aggr_activate(aggr); @@ -857,7 +891,7 @@ gpio_aggr_device_live_store(struct config_item *item, c= onst char *page, * Undepend is required only if device disablement (live =3D=3D 0) * succeeds or if device enablement (live =3D=3D 1) fails. */ - if (live =3D=3D !!ret) + if (live =3D=3D !!ret && !aggr->init_via_sysfs) aggr_lockup_configfs(aggr, false); =20 module_put(THIS_MODULE); @@ -901,7 +935,7 @@ static void gpio_aggr_device_release(struct config_item= *item) struct gpio_aggregator *aggr =3D to_gpio_aggregator(item); =20 /* - * If the aggregator is active, this code wouldn't be reached, + * At this point, aggr is neither active nor activating, * so calling aggr_deactivate() is always unnecessary. */ aggr_free(aggr); @@ -923,6 +957,15 @@ gpio_aggr_device_make_group(struct config_group *group= , const char *name) if (ret !=3D 1 || nchar !=3D strlen(name)) return ERR_PTR(-EINVAL); =20 + if (aggr->init_via_sysfs) + /* + * Aggregators created via legacy sysfs interface are exposed as + * default groups, which means rmdir(2) is prohibited for them. + * For simplicity, and to avoid confusion, we also prohibit + * mkdir(2). + */ + return ERR_PTR(-EPERM); + guard(mutex)(&aggr->lock); =20 if (aggr_is_active(aggr)) @@ -960,6 +1003,14 @@ gpio_aggr_make_group(struct config_group *group, cons= t char *name) struct gpio_aggregator *aggr; int ret; =20 + /* + * "_sysfs" prefix is reserved for auto-generated config group + * for devices create via legacy sysfs interface. + */ + if (strncmp(name, AGGREGATOR_LEGACY_PREFIX, + sizeof(AGGREGATOR_LEGACY_PREFIX)) =3D=3D 0) + return ERR_PTR(-EINVAL); + /* arg space is unneeded */ ret =3D aggr_alloc(&aggr, 0); if (ret) @@ -996,6 +1047,8 @@ static struct configfs_subsystem gpio_aggr_subsys =3D { static int aggr_parse(struct gpio_aggregator *aggr) { char *args =3D skip_spaces(aggr->args); + struct gpio_aggregator_line *line; + char name[CONFIGFS_ITEM_NAME_LEN]; char *key, *offsets, *p; unsigned int i, n =3D 0; int error =3D 0; @@ -1012,9 +1065,24 @@ static int aggr_parse(struct gpio_aggregator *aggr) p =3D get_options(offsets, 0, &error); if (error =3D=3D 0 || *p) { /* Named GPIO line */ + scnprintf(name, sizeof(name), "line%u", n); + line =3D aggr_line_alloc(aggr, n, key, -1); + if (!line) { + error =3D -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggr_line_type); + error =3D configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + aggr_line_add(aggr, line); + error =3D aggr_add_gpio(aggr, key, U16_MAX, &n); if (error) - return error; + goto err; =20 key =3D offsets; continue; @@ -1028,9 +1096,24 @@ static int aggr_parse(struct gpio_aggregator *aggr) } =20 for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + scnprintf(name, sizeof(name), "line%u", n); + line =3D aggr_line_alloc(aggr, n, key, i); + if (!line) { + error =3D -ENOMEM; + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggr_line_type); + error =3D configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + aggr_line_add(aggr, line); + error =3D aggr_add_gpio(aggr, key, i, &n); if (error) - return error; + goto err; } =20 args =3D next_arg(args, &key, &p); @@ -1038,15 +1121,20 @@ static int aggr_parse(struct gpio_aggregator *aggr) =20 if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + goto err; } =20 return 0; + +err: + aggr_free_lines(aggr); + return error; } =20 static ssize_t new_device_store(struct device_driver *driver, const char *= buf, size_t count) { + char name[CONFIGFS_ITEM_NAME_LEN]; struct gpio_aggregator *aggr; struct platform_device *pdev; int res; @@ -1075,10 +1163,24 @@ static ssize_t new_device_store(struct device_drive= r *driver, const char *buf, goto free_table; } =20 - res =3D aggr_parse(aggr); + scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id= ); + config_group_init_type_name(&aggr->group, name, &gpio_aggr_device_type); + + /* + * Since the device created by sysfs might be toggled via configfs + * 'live' attribute later, this initialization is needed. + */ + dev_sync_probe_init(&aggr->probe_data); + + /* Expose to configfs */ + res =3D configfs_register_group(&gpio_aggr_subsys.su_group, &aggr->group); if (res) goto free_dev_id; =20 + res =3D aggr_parse(aggr); + if (res) + goto unregister_group; + gpiod_add_lookup_table(aggr->lookups); =20 pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); @@ -1093,6 +1195,8 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, =20 remove_table: gpiod_remove_lookup_table(aggr->lookups); +unregister_group: + configfs_unregister_group(&aggr->group); free_dev_id: kfree(aggr->lookups->dev_id); free_table: @@ -1108,7 +1212,12 @@ static DRIVER_ATTR_WO(new_device); =20 static void gpio_aggregator_free(struct gpio_aggregator *aggr) { - aggr_deactivate(aggr); + scoped_guard(mutex, &aggr->lock) { + if (aggr_is_activating(aggr) || aggr_is_active(aggr)) + aggr_deactivate(aggr); + } + aggr_free_lines(aggr); + configfs_unregister_group(&aggr->group); kfree(aggr); } =20 --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B2400205E0C for ; Sat, 15 Mar 2025 16:41:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056914; cv=none; b=BSGwq78W045htyGQXNahenZq0FU4l6mYmaMciOVNkOqR7yV/aDG3BoWBvhzoObcZUfNqd6yCdBB8Rt4aznFPRMu1RqCJ/QJXGvutTqANnk8riqhbrr2/q7nPUE99zpIBDurRFd0/8z32jslMIuGQUl1NQlUEeDIW3dK8A02yRtE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056914; c=relaxed/simple; bh=/Z2MAli9EXDhOWQJGX/cq3RhlXJKDrxBGFnuUhIEBNE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FtqeAfJlqAhZ6LrlCFPsfPIg3mKOf9y3EvOlwmeJB66RIhf1X54PMZOZaOfqh+Gho4g0SNWOsJdU9Rf9sEMPDIHOd9Sg37w0Om75DwePFSUrgjOwJ7aT52fFUjzBHG3dSK0UDi3Cj2G61UnnqRJnI/xEgS+fGxgO1YlPKdQSFZ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=DEvMm8Aw; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="DEvMm8Aw" Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 0DC723F2B6 for ; Sat, 15 Mar 2025 16:41:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056911; bh=tGpODGt9i1CwJG7JFJhQeHtJp8NGC2Si4RhlSW04Hrg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DEvMm8AwBTuqXSo5lbzI4FhW9Z7XvCgFiP6HMwphEPPDOzCHvvqS1Kvjv64ZwGfvW nEU8uSeowcfK5CFWId+XjAm3hr1h0+pvCiMnLJl7QzZteZH24BznyT0mI2CFrx39LT SZJqpx4iOHVoB8Oyb0q3KoafYnDDLmrHjL3lFxjJh3ZIPt1R8kfiCbL3mcEelb2DSA idjiIP6skXGw1NrrqVWgNejASMC4Xi2eo+Xorl70f+yTKBmtehAZfJj7V5ZET2pTdo 1mE220NXMUFVO3apI3k6Vk1dgsMTY8QLHdWHd7p9S4vl3zKe+5x56hgsNoT+GTiHoS 9st/jniqVP5yQ== Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-2ff7f9a0b9bso910478a91.0 for ; Sat, 15 Mar 2025 09:41:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056909; x=1742661709; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tGpODGt9i1CwJG7JFJhQeHtJp8NGC2Si4RhlSW04Hrg=; b=WGrWk5HSFCdVksTCFClPYlxdUwWkVIKHRZkgmM/pt4yWYI0nKQWOXkHNYoLoWBarB/ I7r2bRsQUP/Qtaz8Duk/3yDV5DM7wLvbbnrPJYgL71vjfWmunpAkiKLBJI6lDxsB54yF qTo0pXiWs6we/Bpd5sPERk2nQ6JLPwUFMBSgL+Sl1NLmuuITwYmoPmyg9Cszt0nb0tfI iK3+YSRkg7rsbYJ/p3AMDK7qkmZZm4EHNK6a/55yjJU1DbpAtdg157JGJ2ZCXXx/b9Fd Q4RiffUXsxz35JKRgUUDqaU8VKhDZ+Ns9cSgwvmOQwtGPV/FrtJ+C4QHFkSyfnj9gtyx D6lg== X-Forwarded-Encrypted: i=1; AJvYcCUN4Qr56GDiVSkbtrlUbkb7u50RHTWaa4pIl23HlYHzLXUEglTHKHw5cHZwvs7u9SCdsn6JUW0SMw/hhrU=@vger.kernel.org X-Gm-Message-State: AOJu0YwWDhaI4aoQ8TgGkPdD1glSWtQ6ziDnMFVD86IbB26r9iiCXrDr ocf/FQypwx6BMbVQ7NYJogx/R19TfEbPgKEmQRiZIIOz249/6tZpInN0X08Irb3UHKJ+ZPNbdL+ Y8rYTEI71RudQezoJOiruQy+8vM8RvMNFxewZtWfMRrFa3dxSIweduQrvb4fvtxMx9DMYmXXD2A 0BFQ== X-Gm-Gg: ASbGncuh3TX5CNzyzc/DT8VH1EYalrNG8l0J0eXCoOAHJxHMnf9LkZjmzEJNe5/YSbX 8LUwfeeWoqhyYG2U5qoNeOzjtv5cGn3ZcBfYBk5JL7v/uEvzG06WLlVjRxuiqss9FuvdhZyLpVa +YX9kL0oUdowUF57SXP7XcWFC77orkiUDValRbFImf2Y8UaDQRWIKT6X7uUPm3FsTrt2WMpoGLw QR+CL+oQbbyo6JXoK3q2UbTyrWf9ucmauW6J2KIk7Am0O9Ql27ahEpNSFUEe4JY4JyrOBmhOXnA f8gVahLUYH9q8ysU X-Received: by 2002:a05:6a20:8c9e:b0:1f5:873b:3d32 with SMTP id adf61e73a8af0-1f5c13227c6mr6406192637.39.1742056909504; Sat, 15 Mar 2025 09:41:49 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFZOBLI0X8SyCatv7tcjBCLoHnb+mWe3/KR563fonVBZeCW2KmMW/mdZPHgtvnuvOh9i5z4+Q== X-Received: by 2002:a05:6a20:8c9e:b0:1f5:873b:3d32 with SMTP id adf61e73a8af0-1f5c13227c6mr6406179637.39.1742056909207; Sat, 15 Mar 2025 09:41:49 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:48 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 7/9] gpio: aggregator: cancel deferred probe for devices created via configfs Date: Sun, 16 Mar 2025 01:41:21 +0900 Message-ID: <20250315164123.1855142-8-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For aggregators initialized via configfs, write 1 to 'live' waits for probe completion and returns an error if the probe fails, unlike the legacy sysfs interface, which is asynchronous. Since users control the liveness of the aggregator device and might be editing configurations while 'live' is 0, deferred probing is both unnatural and unsafe. Cancel deferred probe for purely configfs-based aggregators when probe fails. Signed-off-by: Koichiro Den --- drivers/gpio/gpio-aggregator.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index de693182070c..89c78d145cbd 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -72,6 +72,10 @@ struct gpio_aggregator_line { enum gpio_lookup_flags flags; }; =20 +struct gpio_aggregator_pdev_meta { + bool init_via_sysfs; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); =20 @@ -1134,6 +1138,7 @@ static int aggr_parse(struct gpio_aggregator *aggr) static ssize_t new_device_store(struct device_driver *driver, const char *= buf, size_t count) { + struct gpio_aggregator_pdev_meta meta =3D { .init_via_sysfs =3D true }; char name[CONFIGFS_ITEM_NAME_LEN]; struct gpio_aggregator *aggr; struct platform_device *pdev; @@ -1183,7 +1188,7 @@ static ssize_t new_device_store(struct device_driver = *driver, const char *buf, =20 gpiod_add_lookup_table(aggr->lookups); =20 - pdev =3D platform_device_register_simple(DRV_NAME, aggr->id, NULL, 0); + pdev =3D platform_device_register_data(NULL, DRV_NAME, aggr->id, &meta, s= izeof(meta)); if (IS_ERR(pdev)) { res =3D PTR_ERR(pdev); goto remove_table; @@ -1266,14 +1271,15 @@ static struct attribute *gpio_aggregator_attrs[] = =3D { }; ATTRIBUTE_GROUPS(gpio_aggregator); =20 - /* * GPIO Aggregator platform device */ =20 static int gpio_aggregator_probe(struct platform_device *pdev) { + struct gpio_aggregator_pdev_meta *meta; struct device *dev =3D &pdev->dev; + bool init_via_sysfs =3D false; struct gpio_desc **descs; struct gpiochip_fwd *fwd; unsigned long features; @@ -1287,10 +1293,28 @@ static int gpio_aggregator_probe(struct platform_de= vice *pdev) if (!descs) return -ENOMEM; =20 + meta =3D dev_get_platdata(&pdev->dev); + if (meta && meta->init_via_sysfs) + init_via_sysfs =3D true; + for (i =3D 0; i < n; i++) { descs[i] =3D devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); - if (IS_ERR(descs[i])) + if (IS_ERR(descs[i])) { + /* + * Deferred probing is not suitable when the aggregator + * is created via configfs. They should just retry later + * whenever they like. For device creation via sysfs, + * error is propagated without overriding for backward + * compatibility. .prevent_deferred_probe is kept unset + * for other cases. + */ + if (!init_via_sysfs && !dev_of_node(dev) && + descs[i] =3D=3D ERR_PTR(-EPROBE_DEFER)) { + pr_warn("Deferred probe canceled for creation via configfs.\n"); + return -ENODEV; + } return PTR_ERR(descs[i]); + } } =20 features =3D (uintptr_t)device_get_match_data(dev); --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DC552063C7 for ; Sat, 15 Mar 2025 16:41:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056916; cv=none; b=XkDAS0RMCIz/0RBIYUi2Wr9lL0/5mLyJugRDBUHtqgn6Veh17AG+Uk+5vi/Y6Krp4J7Dj3tfh8WbVNpYkApSuYfmKKNxDrGNV/yN8TMk3F05eqnhfmzL8Quo8/iQN8JR+qeCEE9IBNuo91IkW09PrxlQzMyBORkyxX0gAfbfxPM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056916; c=relaxed/simple; bh=5z75klJulPBK3euVFo5jCw9QXY35/qZ4NCunDCR/9Jo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fXbY2BHF1B8xYd0YflndiFzmmYQhYwO/O6X2uOAmGddYRKS+KZ/bDHrb6lsatqhWdpU/+OCUjz6gvwKaTnxRWIugutXBNRGYA8E2u4zOW8WEfnw0bgS0ZE4kpnmMtDrlkmOPM7QkkwYOhR57CxtvGKiqS/a4Pg+gpHGdhssvRZc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=HWI1g2br; arc=none smtp.client-ip=185.125.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="HWI1g2br" Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 874983F129 for ; Sat, 15 Mar 2025 16:41:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056913; bh=VbN0B6Oenuw6+py3+N6wdefqtqSm9CuUWAW6b8DJ97M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HWI1g2brrc4J1vRFF0L32zcIhwxrBpGpmZKKzEihfi6aaX+d4+96oCzP6zaIeZROK yECDXyhSgHn44aGXt3ZeU/SllSE6+rtRO272nMlzPMTbEba8ZER3TVMZk2fLpnbMUM /F9Ac2jz+lt5dGnRX0W5+o0uDm+rg8xTGB2QHU/kethZtpk+9GO7tbS6FdFAoggHZT 4C5MPpC4nIkdQGWaTPn5Q8LJHt8pUq4wrmQiR1qTISMGYWYUmLHeywR8zhfQqIRSE8 1p381aMn27wZbfGUaUgxjMDvwqp7EzkZ8NUsnVyBe5kWLTa/m3eQ/k4up06R0Mf/gh 1YmaXrrfGvedQ== Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2235a1f8aadso46886615ad.2 for ; Sat, 15 Mar 2025 09:41:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056912; x=1742661712; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VbN0B6Oenuw6+py3+N6wdefqtqSm9CuUWAW6b8DJ97M=; b=DsAe1U9rlDpQcqmigBOk78u2qmHV6lXg5ScmEfwrV2crMRqoKmm2rIVyvNZEycK8hU Ns0C5PEUv6ImD7ElLnppMK7upvraqVBUCcGCHpHcCySftfpf2KHkJQ6UZxeO58+ZJAg2 iPgoMa4fLzYr2OuIzXaLov0C6WdHARHOI1ZEvTvCzo1HRineozQ1ytgcD7yYrJ+0BRmW a7lO4mpRc+dX5ZENDkQVz8uYNx9Fm5yW9o5dhpxq4FshOyidWHTpLKESiaqUb1VH9gy6 VxuT6fONvb6vggbuRTOTpigv9sYRR4iIStRye6qWCY/F4lflQVk3VPx68sY60gGyIfyG tcpg== X-Forwarded-Encrypted: i=1; AJvYcCV+2m4sXN+04RQwqC+RAdd5cUD2xz8pV2GxhsZWeACR8Av6B7//78thByPms+/tb7avY+jvGJ3x6ig72CI=@vger.kernel.org X-Gm-Message-State: AOJu0YxGjry/IEIcr/0CS2DgTG72pPJ1PkmmE0GqI69Heri2Y1BD0NbS 09wUupz547kpwY/Dl5vbMEzPMCjCOyogp3TGx3NlykUd6sQrMXoC631ZeOv8l9XIfQaiJSFGP4z Ta4HAu/mDTboo7R/CIBwrZoIwFJnDHp+hmiAlpdOxb2jf3vA3bwCliBp5Mb3/G7l9OWe1TwFvfq jQ0g== X-Gm-Gg: ASbGncspmVdsuHPXEhaE67vOcrFVI8BoMdPYdojkDRdMvG2+2lw+XX/7qpChcaIEatg iWJafJlp9lr2wHraj5Lxn4yrZCKjx1FXCrUe14+8+iHclqEeyVfkSrDT/SIho8Pd6HatVO9P2F1 DS0eWp1S8I3kL4REJrMAQd0I7gd4df3KkHmkuMqHTT0SqajmrJB5Lg8gyIETO6stBwJAacU6oVS FNAkp4M96H2gMi6h7ibzL1te4EZKq23sXFdTFphSNU4CX+S8oJgokw54L16ThonlF015+Sn5PrN 6iiD7EZdgzRGwofI X-Received: by 2002:a05:6a00:464f:b0:736:5f75:4a44 with SMTP id d2e1a72fcca58-737223fd0ecmr6839071b3a.22.1742056912028; Sat, 15 Mar 2025 09:41:52 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGZPiPSqUBXWonM+1wni5zrfFyY061fEIcUx8VfreIK0Dp+klWcEbYSYn7e4dG+W0ARwHPvpQ== X-Received: by 2002:a05:6a00:464f:b0:736:5f75:4a44 with SMTP id d2e1a72fcca58-737223fd0ecmr6839052b3a.22.1742056911569; Sat, 15 Mar 2025 09:41:51 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:51 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 8/9] Documentation: gpio: document configfs interface for gpio-aggregator Date: Sun, 16 Mar 2025 01:41:22 +0900 Message-ID: <20250315164123.1855142-9-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add documentation for the newly added configfs-based interface for GPIO aggregator. Signed-off-by: Koichiro Den --- .../admin-guide/gpio/gpio-aggregator.rst | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/Documentation/admin-guide/gpio/gpio-aggregator.rst b/Documenta= tion/admin-guide/gpio/gpio-aggregator.rst index 5cd1e7221756..8374a9df9105 100644 --- a/Documentation/admin-guide/gpio/gpio-aggregator.rst +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst @@ -69,6 +69,113 @@ write-only attribute files in sysfs. $ echo gpio-aggregator.0 > delete_device =20 =20 +Aggregating GPIOs using Configfs +-------------------------------- + +**Group:** ``/config/gpio-aggregator`` + + This is the root directory of the gpio-aggregator configfs tree. + +**Group:** ``/config/gpio-aggregator/`` + + This directory represents a GPIO aggregator device. You can assign any + name to ```` (e.g. ``agg0``), except names starting with + ``_sysfs`` prefix, which are reserved for auto-generated configfs + entries corresponding to devices created via Sysfs. + +**Attribute:** ``/config/gpio-aggregator//live`` + + The ``live`` attribute allows to trigger the actual creation of the de= vice + once it's fully configured. Accepted values are: + + * ``1``, ``yes``, ``true`` : enable the virtual device + * ``0``, ``no``, ``false`` : disable the virtual device + +**Attribute:** ``/config/gpio-aggregator//dev_name`` + + The read-only ``dev_name`` attribute exposes the name of the device as= it + will appear in the system on the platform bus (e.g. ``gpio-aggregator.= 0``). + This is useful for identifying a character device for the newly created + aggregator. If it's ``gpio-aggregator.0``, + ``/sys/devices/platform/gpio-aggregator.0/gpiochipX`` path tells you t= hat the + GPIO device id is ``X``. + +You must create subdirectories for each virtual line you want to +instantiate, named exactly as ``line0``, ``line1``, ..., ``lineY``, when +you want to instantiate ``Y+1`` (Y >=3D 0) lines. Configure all lines bef= ore +activating the device by setting ``live`` to 1. + +**Group:** ``/config/gpio-aggregator///`` + + This directory represents a GPIO line to include in the aggregator. + +**Attribute:** ``/config/gpio-aggregator///key`` + +**Attribute:** ``/config/gpio-aggregator///offset`` + + The default values after creating the ```` directory are: + + * ``key`` : + * ``offset`` : -1 + + ``key`` must always be explicitly configured, while ``offset`` depends. + Two configuration patterns exist for each ````: + + (a). For lookup by GPIO line name: + + * Set ``key`` to the line name. + * Ensure ``offset`` remains -1 (the default). + + (b). For lookup by GPIO chip name and the line offset within the chip: + + * Set ``key`` to the chip name. + * Set ``offset`` to the line offset (0 <=3D ``offset`` < 65535). + +**Attribute:** ``/config/gpio-aggregator///name`` + + The ``name`` attribute sets a custom name for lineY. If left unset, the + line will remain unnamed. + +Once the configuration is done, the ``'live'`` attribute must be set to 1 +in order to instantiate the aggregator device. It can be set back to 0 to +destroy the virtual device. The module will synchronously wait for the new +aggregator device to be successfully probed and if this doesn't happen, wr= iting +to ``'live'`` will result in an error. This is a different behaviour from = the +case when you create it using sysfs ``new_device`` interface. + +.. note:: + + For aggregators created via Sysfs, the configfs entries are + auto-generated and appear as ``/config/gpio-aggregator/_sysfs./``. Y= ou + cannot add or remove line directories with mkdir(2)/rmdir(2). To modify + lines, you must use the "delete_device" interface to tear down the + existing device and reconfigure it from scratch. However, you can still + toggle the aggregator with the ``live`` attribute and adjust the + ``key``, ``offset``, and ``name`` attributes for each line when ``live`` + is set to 0 by hand (i.e. it's not waiting for deferred probe). + +Sample configuration commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: sh + + # Create a directory for an aggregator device + $ mkdir /sys/kernel/config/gpio-aggregator/agg0 + + # Configure each line + $ mkdir /sys/kernel/config/gpio-aggregator/agg0/line0 + $ echo gpiochip0 > /sys/kernel/config/gpio-aggregator/agg0/line0/key + $ echo 6 > /sys/kernel/config/gpio-aggregator/agg0/line0/offset + $ echo test0 > /sys/kernel/config/gpio-aggregator/agg0/line0/name + $ mkdir /sys/kernel/config/gpio-aggregator/agg0/line1 + $ echo gpiochip0 > /sys/kernel/config/gpio-aggregator/agg0/line1/key + $ echo 7 > /sys/kernel/config/gpio-aggregator/agg0/line1/offset + $ echo test1 > /sys/kernel/config/gpio-aggregator/agg0/line1/name + + # Activate the aggregator device + $ echo 1 > /sys/kernel/config/gpio-aggregator/agg0/live + + Generic GPIO Driver ------------------- =20 --=20 2.45.2 From nobody Wed Dec 17 17:27:56 2025 Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 748792066D9 for ; Sat, 15 Mar 2025 16:41:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.125.188.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056922; cv=none; b=Slcc+ZziBy1ZdCEl8iEunZtFsVaL4GLvsuNyg9Y+rB4+nxaO9CkNbKywsLWEW0y+Gqw17CpzJZs1JVxJqtcPVK2ObqeCymo64EbD7NzHrPAoReDxoL/ErrMNUdlxGF0JkiwWzbJ0EKZNSt2A4W1JZEHC1v2AjBc+01Q6WZYeEnM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742056922; c=relaxed/simple; bh=vrVAGSgxNANrEDlAUI4hvwiL+VnhDEgL6Y/zsKBMygU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JQ2ogknSs8+1bZGKyW9qRwfm4y63PW5IkaFJBBeFJdyfgBbo1o0W8tqZ00c9Bp1peIs7hc4Qc33wyD9Goooh2zEAzdjdJFbHKW1ujnlVuICF8yTz7LM5JauQ8QvbzLKVjZa4A8Yun2l24ZBk/jnd3xFgw6ozBl6RsZWG+uJPT38= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com; spf=pass smtp.mailfrom=canonical.com; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b=jo1Kveol; arc=none smtp.client-ip=185.125.188.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=canonical.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=canonical.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=canonical.com header.i=@canonical.com header.b="jo1Kveol" Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 849993F2B1 for ; Sat, 15 Mar 2025 16:41:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1742056916; bh=unE8r5bz7y+LtS0Ck6ChgHm8lSL9zhWWp92j38DBeMA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jo1KveolHiOGO76RAto2TxNCVc3vtxMGvU1ynC2Y8zrAYhjPeZmh7YdkU5rdq9WX0 Z2YdfMwqy9uiDs5Z/GjiWwF++SBmuNcVswYw7/ri1jyyH4013eYsjRHlqLItg26hvA APO4sYgIf2aGM/+WgYPnV67wJA9KXhhM2J/PHqRShHV9mTZLaims3hLD/Yyezqz1Ru QzlDvmEsU2Y5Wht0rmm6zg60Uhl+/F43VvGi2aCBDVdQQz73IZq+wFLbKEMSPAF7Gp XO4Oaf9gvuiZLen2N7/Fpys5KaF+ZvkahmVRLSNaKdC+hCFzg8wGr4Vk3a9gIJk27B 2+dsrjRx4ZhpA== Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-2ff605a7a43so1923871a91.3 for ; Sat, 15 Mar 2025 09:41:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742056915; x=1742661715; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=unE8r5bz7y+LtS0Ck6ChgHm8lSL9zhWWp92j38DBeMA=; b=ZEhE4iS7hG97LKMoC89BKOYv+gi1EUCE5i38o7W7AbRGfQOL/AFV7ssCwsIAowz5wd XJFTfksltm6StaGuAOXAHlT4DwuD08Fa0GbowX74gxZueYxjyHgg1gEk9hHnZLVwsi4p YIyyyTI5XZEb/RARfnxdclqDrp+/yYsqpJP2hC52KgiMJgx1Bnm4WNf/U2IDKWchDG6w ZKvojzqf23zGo3/YTtLIVTfWvlPL1p0PrklOicpIq5ANJNimJ429hkJk2F2EEZjd+nax +n/2efer6f9TsR0tPj6EQJwAH4ZI7aIBr6VbJW83+AVxuitITVmbzsCBK/FANYUvuRXk kKHA== X-Forwarded-Encrypted: i=1; AJvYcCVrLl79GSN7QyIJaRkeCDw0Pl75WOiI9Lo/766pRIUnosRcBW5BAGIBK28Thk5CPQ2X8uT8aVTMCmWT0qo=@vger.kernel.org X-Gm-Message-State: AOJu0YwRSSbfS2AEHoI9Xucc6Ij5N8PRC5joSMJlTBeZQ3QylGVukOMU 4xQyqA4ezm6Sg4z5NY7sPD/WD0FkVpeDEYY3/TzeTpsRDJ+W+QIOHkmz7qKthVqHbh+x9lJcERs rFSNpIggB92eDIKE6E4SHD0+ETALNkCHRy81sr8GGgdpope3tegoQALUONYofGeooZdTZ8Ts+IY BK0nL/Ag/tew== X-Gm-Gg: ASbGncvsBTH9I5BnTp8x/1MPd26Bqj26b3v7p/zgbwSK1t5Xf+RXKyG3MTVC/AMsgmn ttQCwi0Ud/QxFxqRqd2U8L6xTUoVW7h1B/7w/Do+LYqBbDolaFtKff+2k4ytG0FlazF2kKH2d3F qTjC3T7rzPAKCFIcLt5x889JoCnCx44vfpDtG516f4OCDUX6VW59/Aji/0yEG7toA411WqV2S6K q2IKxiW41OaqwplC3/q8l7A9DqFXDsz/Gs5u1IpQ9Fv8PDjk2eEfojVQEtg0KhpIKeRrCnjXAtO 8YI2TRi8STngiykL X-Received: by 2002:a05:6a21:2d88:b0:1f5:80eb:8481 with SMTP id adf61e73a8af0-1f5c1183d83mr9738452637.13.1742056914351; Sat, 15 Mar 2025 09:41:54 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHCEZ+jCNfkggEZRE2XA8z/q0f4hP43Uiof7JH5pG0+MQxfwoXLZQT+QTzoV2rZZ4l14PcTHg== X-Received: by 2002:a05:6a21:2d88:b0:1f5:80eb:8481 with SMTP id adf61e73a8af0-1f5c1183d83mr9738427637.13.1742056913902; Sat, 15 Mar 2025 09:41:53 -0700 (PDT) Received: from z790sl.. ([240f:74:7be:1:83e6:3590:3498:db44]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371155e3a9sm4637104b3a.71.2025.03.15.09.41.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Mar 2025 09:41:53 -0700 (PDT) From: Koichiro Den To: linux-gpio@vger.kernel.org Cc: brgl@bgdev.pl, geert+renesas@glider.be, linus.walleij@linaro.org, maciej.borzecki@canonical.com, linux-kernel@vger.kernel.org Subject: [PATCH v6 9/9] selftests: gpio: add test cases for gpio-aggregator Date: Sun, 16 Mar 2025 01:41:23 +0900 Message-ID: <20250315164123.1855142-10-koichiro.den@canonical.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20250315164123.1855142-1-koichiro.den@canonical.com> References: <20250315164123.1855142-1-koichiro.den@canonical.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a set of tests for gpio-aggregator module. This test covers both pre-existing new_device/delete_device interface and new configfs-based interface. Signed-off-by: Koichiro Den --- tools/testing/selftests/gpio/Makefile | 2 +- tools/testing/selftests/gpio/config | 1 + .../testing/selftests/gpio/gpio-aggregator.sh | 723 ++++++++++++++++++ 3 files changed, 725 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/gpio/gpio-aggregator.sh diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftest= s/gpio/Makefile index e0884390447d..7bfe315f7001 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 =20 -TEST_PROGS :=3D gpio-mockup.sh gpio-sim.sh +TEST_PROGS :=3D gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh TEST_FILES :=3D gpio-mockup-sysfs.sh TEST_GEN_PROGS_EXTENDED :=3D gpio-mockup-cdev gpio-chip-info gpio-line-name CFLAGS +=3D -O2 -g -Wall $(KHDR_INCLUDES) diff --git a/tools/testing/selftests/gpio/config b/tools/testing/selftests/= gpio/config index 409a8532facc..1287abeaac7e 100644 --- a/tools/testing/selftests/gpio/config +++ b/tools/testing/selftests/gpio/config @@ -2,3 +2,4 @@ CONFIG_GPIOLIB=3Dy CONFIG_GPIO_CDEV=3Dy CONFIG_GPIO_MOCKUP=3Dm CONFIG_GPIO_SIM=3Dm +CONFIG_GPIO_AGGREGATOR=3Dm diff --git a/tools/testing/selftests/gpio/gpio-aggregator.sh b/tools/testin= g/selftests/gpio/gpio-aggregator.sh new file mode 100755 index 000000000000..f1bab62c4cd2 --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-aggregator.sh @@ -0,0 +1,723 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Bartosz Golaszewski +# Copyright (C) 2025 Koichiro Den + +BASE_DIR=3D$(dirname "$0") +CONFIGFS_SIM_DIR=3D"/sys/kernel/config/gpio-sim" +CONFIGFS_AGG_DIR=3D"/sys/kernel/config/gpio-aggregator" +SYSFS_AGG_DIR=3D"/sys/bus/platform/drivers/gpio-aggregator" +MODULE=3D"gpio-aggregator" + +fail() { + echo "$*" >&2 + echo "GPIO $MODULE test FAIL" + exit 1 +} + +skip() { + echo "$*" >&2 + echo "GPIO $MODULE test SKIP" + exit 4 +} + +# gpio-sim +sim_enable_chip() { + local CHIP=3D$1 + + echo 1 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to enable the chi= p" +} + +sim_disable_chip() { + local CHIP=3D$1 + + echo 0 > "$CONFIGFS_SIM_DIR/$CHIP/live" || fail "Unable to disable the ch= ip" +} + +sim_configfs_cleanup() { + local NOCHECK=3D${1:-0} + + for CHIP_DIR in "$CONFIGFS_SIM_DIR"/*; do + [ -d "$CHIP_DIR" ] || continue + echo 0 > "$CHIP_DIR/live" + find "$CHIP_DIR" -depth -type d -exec rmdir {} \; + done + [ "$NOCHECK" -eq 1 ] && return; + remaining=3D$(find "$CONFIGFS_SIM_DIR" -mindepth 1 -type d 2> /dev/null) + if [ -n "$remaining" ]; then + fail "Directories remain in $CONFIGFS_SIM_DIR: $remaining" + fi +} + +sim_get_chip_label() { + local CHIP=3D$1 + local BANK=3D$2 + local CHIP_NAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name" 2> /dev= /null) || \ + fail "Unable to read the chip name from configfs" + + $BASE_DIR/gpio-chip-info "/dev/$CHIP_NAME" label || \ + fail "Unable to read the chip label from the character device" +} + +# gpio-aggregator +agg_create_chip() { + local CHIP=3D$1 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP" +} + +agg_remove_chip() { + local CHIP=3D$1 + + find "$CONFIGFS_AGG_DIR/$CHIP/" -depth -type d -exec rmdir {} \; || \ + fail "Unable to remove $CONFIGFS_AGG_DIR/$CHIP" +} + +agg_create_line() { + local CHIP=3D$1 + local LINE=3D$2 + + mkdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_remove_line() { + local CHIP=3D$1 + local LINE=3D$2 + + rmdir "$CONFIGFS_AGG_DIR/$CHIP/$LINE" +} + +agg_set_key() { + local CHIP=3D$1 + local LINE=3D$2 + local KEY=3D$3 + + echo "$KEY" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/key" || fail "Unable to set = the lookup key" +} + +agg_set_offset() { + local CHIP=3D$1 + local LINE=3D$2 + local OFFSET=3D$3 + + echo "$OFFSET" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/offset" || \ + fail "Unable to set the lookup offset" +} + +agg_set_line_name() { + local CHIP=3D$1 + local LINE=3D$2 + local NAME=3D$3 + + echo "$NAME" > "$CONFIGFS_AGG_DIR/$CHIP/$LINE/name" || fail "Unable to se= t the line name" +} + +agg_enable_chip() { + local CHIP=3D$1 + + echo 1 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to enable the chi= p" +} + +agg_disable_chip() { + local CHIP=3D$1 + + echo 0 > "$CONFIGFS_AGG_DIR/$CHIP/live" || fail "Unable to disable the ch= ip" +} + +agg_configfs_cleanup() { + local NOCHECK=3D${1:-0} + + for CHIP_DIR in "$CONFIGFS_AGG_DIR"/*; do + [ -d "$CHIP_DIR" ] || continue + echo 0 > "$CHIP_DIR/live" 2> /dev/null + find "$CHIP_DIR" -depth -type d -exec rmdir {} \; + done + [ "$NOCHECK" -eq 1 ] && return; + remaining=3D$(find "$CONFIGFS_AGG_DIR" -mindepth 1 -type d 2> /dev/null) + if [ -n "$remaining" ]; then + fail "Directories remain in $CONFIGFS_AGG_DIR: $remaining" + fi +} + +agg_configfs_dev_name() { + local CHIP=3D$1 + + cat "$CONFIGFS_AGG_DIR/$CHIP/dev_name" 2> /dev/null || \ + fail "Unable to read the device name from configfs" +} + +agg_configfs_chip_name() { + local CHIP=3D$1 + local DEV_NAME=3D$(agg_configfs_dev_name "$CHIP") + local CHIP_LIST=3D$(find "/sys/devices/platform/$DEV_NAME" \ + -maxdepth 1 -type d -name "gpiochip[0-9]*" 2> /dev/null) + local CHIP_COUNT=3D$(echo "$CHIP_LIST" | wc -l) + + if [ -z "$CHIP_LIST" ]; then + fail "No gpiochip in /sys/devices/platform/$DEV_NAME/" + elif [ "$CHIP_COUNT" -ne 1 ]; then + fail "Multiple gpiochips unexpectedly found: $CHIP_LIST" + fi + basename "$CHIP_LIST" +} + +agg_get_chip_num_lines() { + local CHIP=3D$1 + local N_DIR=3D$(ls -d $CONFIGFS_AGG_DIR/$CHIP/line[0-9]* 2> /dev/null | w= c -l) + local N_LINES + + if [ "$(cat $CONFIGFS_AGG_DIR/$CHIP/live)" =3D 0 ]; then + echo "$N_DIR" + else + N_LINES=3D$( + $BASE_DIR/gpio-chip-info \ + "/dev/$(agg_configfs_chip_name "$CHIP")" num-lines + ) || fail "Unable to read the number of lines from the character device" + if [ $N_DIR !=3D $N_LINES ]; then + fail "Discrepancy between two sources for the number of lines" + fi + echo "$N_LINES" + fi +} + +agg_get_chip_label() { + local CHIP=3D$1 + + $BASE_DIR/gpio-chip-info "/dev/$(agg_configfs_chip_name "$CHIP")" label |= | \ + fail "Unable to read the chip label from the character device" +} + +agg_get_line_name() { + local CHIP=3D$1 + local OFFSET=3D$2 + local NAME_CONFIGFS=3D$(cat "$CONFIGFS_AGG_DIR/$CHIP/line${OFFSET}/name") + local NAME_CDEV + + if [ "$(cat "$CONFIGFS_AGG_DIR/$CHIP/live")" =3D 0 ]; then + echo "$NAME_CONFIGFS" + else + NAME_CDEV=3D$( + $BASE_DIR/gpio-line-name \ + "/dev/$(agg_configfs_chip_name "$CHIP")" "$OFFSET" + ) || fail "Unable to read the line name from the character device" + if [ "$NAME_CONFIGFS" !=3D "$NAME_CDEV" ]; then + fail "Discrepancy between two sources for the name of line" + fi + echo "$NAME_CDEV" + fi +} + + +# Load the modules. This will pull in configfs if needed too. +modprobe gpio-sim || skip "unable to load the gpio-sim module" +modprobe gpio-aggregator || skip "unable to load the gpio-aggregator modul= e" + +# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if neede= d. +for IDX in $(seq 5); do + if [ "$IDX" -eq "5" ]; then + skip "configfs not mounted at /sys/kernel/config" + fi + + mountpoint -q /sys/kernel/config && break + sleep 0.1 +done + +# If the module was already loaded: remove all previous chips +agg_configfs_cleanup +sim_configfs_cleanup + +trap "exit 1" SIGTERM SIGINT +trap "agg_configfs_cleanup 1; sim_configfs_cleanup 1" EXIT + +# Use gpio-sim chips as the test backend +for CHIP in $(seq -f "chip%g" 0 1); do + mkdir $CONFIGFS_SIM_DIR/$CHIP + for BANK in $(seq -f "bank%g" 0 1); do + mkdir -p "$CONFIGFS_SIM_DIR/$CHIP/$BANK" + echo "${CHIP}_${BANK}" > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/label" || \ + fail "unable to set the chip label" + echo 16 > "$CONFIGFS_SIM_DIR/$CHIP/$BANK/num_lines" || \ + fail "unable to set the number of lines" + for IDX in $(seq 0 15); do + LINE_NAME=3D"${CHIP}${BANK}_${IDX}" + LINE_DIR=3D"$CONFIGFS_SIM_DIR/$CHIP/$BANK/line$IDX" + mkdir -p $LINE_DIR + echo "$LINE_NAME" > "$LINE_DIR/name" || fail "unable to set the line na= me" + done + done + sim_enable_chip "$CHIP" +done + +echo "1. GPIO aggregator creation/deletion" + +echo "1.1. Creation/deletion via configfs" + +echo "1.1.1. Minimum creation/deletion" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" +agg_set_offset agg0 line0 5 +agg_set_line_name agg0 line0 test0 +agg_enable_chip agg0 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 1 || fail "chip unexpected= ly dead" +test "$(agg_get_chip_label agg0)" =3D "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" =3D "1" || fail "number of lines is = not 1" +test "$(agg_get_line_name agg0 0)" =3D "test0" || fail "line name is unset" +agg_disable_chip agg0 +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.1.2. Complex creation/deletion" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_create_line agg0 line1 +agg_create_line agg0 line2 +agg_create_line agg0 line3 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" +agg_set_key agg0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_key agg0 line2 "$(sim_get_chip_label chip1 bank0)" +agg_set_key agg0 line3 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset agg0 line0 1 +agg_set_offset agg0 line1 3 +agg_set_offset agg0 line2 5 +agg_set_offset agg0 line3 7 +agg_set_line_name agg0 line0 test0 +agg_set_line_name agg0 line1 test1 +agg_set_line_name agg0 line2 test2 +agg_set_line_name agg0 line3 test3 +agg_enable_chip agg0 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 1 || fail "chip unexpected= ly dead" +test "$(agg_get_chip_label agg0)" =3D "$(agg_configfs_dev_name agg0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines agg0)" =3D "4" || fail "number of lines is = not 1" +test "$(agg_get_line_name agg0 0)" =3D "test0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" =3D "test1" || fail "line name is unset" +test "$(agg_get_line_name agg0 2)" =3D "test2" || fail "line name is unset" +test "$(agg_get_line_name agg0 3)" =3D "test3" || fail "line name is unset" +agg_disable_chip agg0 +agg_remove_line agg0 line0 +agg_remove_line agg0 line1 +agg_remove_line agg0 line2 +agg_remove_line agg0 line3 +agg_remove_chip agg0 + +echo "1.1.3. Can't instantiate a chip without any line" +agg_create_chip agg0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +agg_remove_chip agg0 + +echo "1.1.4. Can't instantiate a chip with invalid configuration" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "chipX_bankX" +agg_set_offset agg0 line0 99 +agg_set_line_name agg0 line0 test0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.1.5. Can't instantiate a chip asynchronously via deferred probe" +agg_create_chip agg0 +agg_create_line agg0 line0 +agg_set_key agg0 line0 "chip0_bank0" +agg_set_offset agg0 line0 5 +agg_set_line_name agg0 line0 test0 +sim_disable_chip chip0 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || fail "chip unexpected= ly alive" +sim_enable_chip chip0 +sleep 1 +test "$(cat "$CONFIGFS_AGG_DIR/agg0/live")" =3D 0 || \ + fail "chip unexpectedly transitioned to 'live' state" +agg_remove_line agg0 line0 +agg_remove_chip agg0 + +echo "1.2. Creation/deletion via sysfs" + +echo "1.2.1. Minimum creation/deletion" +echo "chip0_bank0 0" > "$SYSFS_AGG_DIR/new_device" +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "1" || fail "number of lines= is not 1" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name is unset" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.2. Complex creation/deletion" +echo "chip0bank0_0 chip1_bank1 10-11" > "$SYSFS_AGG_DIR/new_device" +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "3" || fail "number of lines= is not 3" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 1)" =3D "" || fail "line name is unset" +test "$(agg_get_line_name _sysfs.0 2)" =3D "" || fail "line name is unset" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.3. Asynchronous creation with deferred probe" +sim_disable_chip chip0 +echo 'chip0_bank0 0' > $SYSFS_AGG_DIR/new_device +sleep 1 +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 0 || fail "chip unexpe= ctedly alive" +sim_enable_chip chip0 +sleep 1 +CHIPNAME=3D$(agg_configfs_chip_name _sysfs.0) +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 1 || fail "chip unexpe= ctedly remains dead" +test "$(agg_get_chip_label _sysfs.0)" =3D "$(agg_configfs_dev_name _sysfs.= 0)" || \ + fail "label is inconsistent" +test "$(agg_get_chip_num_lines _sysfs.0)" =3D "1" || fail "number of lines= is not 1" +test "$(agg_get_line_name _sysfs.0 0)" =3D "" || fail "line name unexpecte= dly set" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +test -d $CONFIGFS_AGG_DIR/_sysfs.0 && fail "_sysfs.0 unexpectedly remains" +test -d /dev/${CHIPNAME} && fail "/dev/${CHIPNAME} unexpectedly remains" + +echo "1.2.4. Can't instantiate a chip with invalid configuration" +echo "xyz 0" > "$SYSFS_AGG_DIR/new_device" +test "$(cat $CONFIGFS_AGG_DIR/_sysfs.0/live)" =3D 0 || fail "chip unexpect= edly alive" +echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" + +echo "2. GPIO aggregator configuration" + +echo "2.1. Configuring aggregators instantiated via configfs" +setup_2_1() { + agg_create_chip agg0 + agg_create_line agg0 line0 + agg_create_line agg0 line1 + agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank0)" + agg_set_key agg0 line1 "$(sim_get_chip_label chip1 bank0)" + agg_set_offset agg0 line0 1 + agg_set_offset agg0 line1 3 + agg_set_line_name agg0 line0 test0 + agg_set_line_name agg0 line1 test1 + agg_enable_chip agg0 +} +teardown_2_1() { + agg_configfs_cleanup +} + +echo "2.1.1. While offline" + +echo "2.1.1.1. Line can be added/removed" +setup_2_1 +agg_disable_chip agg0 +agg_create_line agg0 line2 +agg_set_key agg0 line2 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line2 5 +agg_enable_chip agg0 +test "$(agg_get_chip_num_lines agg0)" =3D "3" || fail "number of lines is = not 1" +teardown_2_1 + +echo "2.1.1.2. Line key can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip0 bank1)" +agg_set_key agg0 line1 "$(sim_get_chip_label chip1 bank1)" +agg_enable_chip agg0 +teardown_2_1 + +echo "2.1.1.3. Line name can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_line_name agg0 line0 new0 +agg_set_line_name agg0 line1 new1 +agg_enable_chip agg0 +test "$(agg_get_line_name agg0 0)" =3D "new0" || fail "line name is unset" +test "$(agg_get_line_name agg0 1)" =3D "new1" || fail "line name is unset" +teardown_2_1 + +echo "2.1.1.4. Line offset can be modified" +setup_2_1 +agg_disable_chip agg0 +agg_set_offset agg0 line0 5 +agg_set_offset agg0 line1 7 +agg_enable_chip agg0 +teardown_2_1 + +echo "2.1.1.5. Can re-enable a chip after valid reconfiguration" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset agg0 line0 15 +agg_set_key agg0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line0 14 +agg_create_line agg0 line2 +agg_set_key agg0 line2 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset agg0 line2 13 +agg_enable_chip agg0 +test "$(agg_get_chip_num_lines agg0)" =3D "3" || fail "number of lines is = not 1" +teardown_2_1 + +echo "2.1.1.7. Can't re-enable a chip with invalid reconfiguration" +setup_2_1 +agg_disable_chip agg0 +agg_set_key agg0 line0 invalidkey +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +teardown_2_1 +setup_2_1 +agg_disable_chip agg0 +agg_set_offset agg0 line0 99 +echo 1 > "$CONFIGFS_AGG_DIR/agg0/live" 2> /dev/null && fail "chip unexpect= edly enabled" +teardown_2_1 + +echo "2.1.2. While online" + +echo "2.1.2.1. Can't add/remove line" +setup_2_1 +mkdir "$CONFIGFS_AGG_DIR/agg0/line2" 2> /dev/null && fail "line unexpected= ly added" +rmdir "$CONFIGFS_AGG_DIR/agg0/line1" 2> /dev/null && fail "line unexpected= ly removed" +teardown_2_1 + +echo "2.1.2.2. Can't modify line key" +setup_2_1 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/agg0/line0/key" 2> /dev/null && \ + fail "lookup key unexpectedly updated" +teardown_2_1 + +echo "2.1.2.3. Can't modify line name" +setup_2_1 +echo "new0" > "$CONFIGFS_AGG_DIR/agg0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_1 + +echo "2.1.2.4. Can't modify line offset" +setup_2_1 +echo "5" > "$CONFIGFS_AGG_DIR/agg0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_1 + +echo "2.2. Configuring aggregators instantiated via sysfs" +setup_2_2() { + echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device" +} +teardown_2_2() { + echo "$(agg_configfs_dev_name _sysfs.0)" > "$SYSFS_AGG_DIR/delete_device" +} + +echo "2.2.1. While online" + +echo "2.2.1.1. Can toggle live" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.1.2. Can't add/remove line" +setup_2_2 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 + +echo "2.2.1.3. Can't modify line key" +setup_2_2 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null &= & \ + fail "lookup key unexpectedly updated" +teardown_2_2 + +echo "2.2.1.4. Can't modify line name" +setup_2_2 +echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_2 + +echo "2.2.1.5. Can't modify line offset" +setup_2_2 +echo "5" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_2 + +echo "2.2.2. While waiting for deferred probe" + +echo "2.2.2.1. Can't add/remove line despite live =3D 0" +sim_disable_chip chip0 +setup_2_2 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.2. Can't modify line key" +sim_disable_chip chip0 +setup_2_2 +echo "chip1_bank1" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/key" 2> /dev/null &= & \ + fail "lookup key unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.3. Can't modify line name" +sim_disable_chip chip0 +setup_2_2 +echo "new0" > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/name" 2> /dev/null && \ + fail "name unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.4. Can't modify line offset" +sim_disable_chip chip0 +setup_2_2 +echo 5 > "$CONFIGFS_AGG_DIR/_sysfs.0/line0/offset" 2> /dev/null && \ + fail "offset unexpectedly updated" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.2.5. Can't toggle live" +sim_disable_chip chip0 +setup_2_2 +test "$(cat "$CONFIGFS_AGG_DIR/_sysfs.0/live")" =3D 0 || fail "chip unexpe= ctedly alive" +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 +sim_enable_chip chip0 + +echo "2.2.3. While offline" + +echo "2.2.3.1. Can't add/remove line despite live =3D 0" +setup_2_2 +agg_disable_chip _sysfs.0 +mkdir "$CONFIGFS_AGG_DIR/_sysfs.0/line2" 2> /dev/null && fail "line unexpe= ctedly added" +rmdir "$CONFIGFS_AGG_DIR/_sysfs.0/line1" 2> /dev/null && fail "line unexpe= ctedly removed" +teardown_2_2 + +echo "2.2.3.2. Line key can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 "$(sim_get_chip_label chip0 bank1)" +agg_set_key _sysfs.0 line1 "$(sim_get_chip_label chip1 bank1)" +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.3. Line name can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_line_name _sysfs.0 line0 new0 +agg_set_line_name _sysfs.0 line1 new1 +agg_enable_chip _sysfs.0 +test "$(agg_get_line_name _sysfs.0 0)" =3D "new0" || fail "line name is un= set" +test "$(agg_get_line_name _sysfs.0 1)" =3D "new1" || fail "line name is un= set" +teardown_2_2 + +echo "2.2.3.4. Line offset can be modified" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_offset _sysfs.0 line0 5 +agg_set_offset _sysfs.0 line1 7 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.5. Can re-enable a chip with valid reconfiguration" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 "$(sim_get_chip_label chip1 bank1)" +agg_set_offset _sysfs.0 line0 15 +agg_set_key _sysfs.0 line1 "$(sim_get_chip_label chip0 bank1)" +agg_set_offset _sysfs.0 line0 14 +agg_enable_chip _sysfs.0 +teardown_2_2 + +echo "2.2.3.6. Can't re-enable a chip with invalid reconfiguration" +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_key _sysfs.0 line0 invalidkey +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 +setup_2_2 +agg_disable_chip _sysfs.0 +agg_set_offset _sysfs.0 line0 99 +echo 1 > "$CONFIGFS_AGG_DIR/_sysfs.0/live" 2> /dev/null && fail "chip unex= pectedly enabled" +teardown_2_2 + +echo "3. Module unload" + +echo "3.1. Can't unload module if there is at least one device created via= configfs" +agg_create_chip agg0 +modprobe -r gpio-aggregator 2> /dev/null +test -d /sys/module/gpio_aggregator || fail "module unexpectedly unloaded" +agg_remove_chip agg0 + +echo "3.2. Can unload module if there is no device created via configfs" +echo "chip0_bank0 1 chip1_bank0 3" > "$SYSFS_AGG_DIR/new_device" +modprobe -r gpio-aggregator 2> /dev/null +test -d /sys/module/gpio_aggregator && fail "module unexpectedly remains t= o be loaded" +modprobe gpio-aggregator 2> /dev/null + +echo "4. GPIO forwarder functional" +SETTINGS=3D"chip0:bank0:2 chip0:bank1:4 chip1:bank0:6 chip1:bank1:8" +setup_4() { + local OFFSET=3D0 + agg_create_chip agg0 + for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + agg_create_line agg0 "line${OFFSET}" + agg_set_key agg0 "line${OFFSET}" "$(sim_get_chip_label "$CHIP" "$BAN= K")" + agg_set_offset agg0 "line${OFFSET}" "$LINE" + OFFSET=3D$(expr $OFFSET + 1) + done + agg_enable_chip agg0 +} +teardown_4() { + agg_configfs_cleanup +} + +echo "4.1. Forwarding set values" +setup_4 +OFFSET=3D0 +for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + DEVNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH=3D"/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/valu= e" + test $(cat $VAL_PATH) =3D "0" || fail "incorrect value read from sysfs" + $BASE_DIR/gpio-mockup-cdev -s 1 "/dev/$(agg_configfs_chip_name agg0)" "$O= FFSET" & + mock_pid=3D$! + sleep 0.1 # FIXME Any better way? + test "$(cat $VAL_PATH)" =3D "1" || fail "incorrect value read from sysfs" + kill "$mock_pid" + OFFSET=3D$(expr $OFFSET + 1) +done +teardown_4 + +echo "4.2. Forwarding set config" +setup_4 +OFFSET=3D0 +for SETTING in $SETTINGS; do + CHIP=3D$(echo "$SETTING" | cut -d: -f1) + BANK=3D$(echo "$SETTING" | cut -d: -f2) + LINE=3D$(echo "$SETTING" | cut -d: -f3) + DEVNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/dev_name") + CHIPNAME=3D$(cat "$CONFIGFS_SIM_DIR/$CHIP/$BANK/chip_name") + VAL_PATH=3D"/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio${LINE}/valu= e" + $BASE_DIR/gpio-mockup-cdev -b pull-up "/dev/$(agg_configfs_chip_name agg0= )" "$OFFSET" + test $(cat "$VAL_PATH") =3D "1" || fail "incorrect value read from sysfs" + OFFSET=3D$(expr $OFFSET + 1) +done +teardown_4 + +echo "5. Race condition verification" + +echo "5.1. Stress test of new_device/delete_device and module load/unload" +for _ in $(seq 1000); do + { + echo "dummy 0" > "$SYSFS_AGG_DIR/new_device" + cat "$CONFIGFS_AGG_DIR/_sysfs.0/dev_name" > "$SYSFS_AGG_DIR/delete_devic= e" + } 2> /dev/null +done & +writer_pid=3D$! +while kill -0 "$writer_pid" 2> /dev/null; do + { + modprobe gpio-aggregator + modprobe -r gpio-aggregator + } 2> /dev/null +done + +echo "GPIO $MODULE test PASS" --=20 2.45.2