* [PATCH 0/5] firewire: cdev: refactor for bus reset event filling
@ 2026-09-20 7:14 Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 1/5] firewire: cdev: use memory barrier to access fw_device members Takashi Sakamoto
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:14 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
Hi,
In the cdev layer, there are two places where bus reset events are filed;
the update path for the associated fw_device instance and the get_info
ioctl call. There is still some code that can be refactored in these
paths.
This series refactors the code to make the memory ordering and locking
scope more explicit, and adds a KUnit test for the bus reset event UAPI
structure.
Takashi Sakamoto (5):
firewire: cdev: use memory barrier to access fw_device members
firewire: cdev: refactor using scoped_guard() for get_info ioctl call
firewire: cdev: refactor copy_to_user() in get_info ioctl call
firewire: cdev: add annotations and comments about race condition of
bus_reset_closure
firewire: cdev: add KUnit test for bus reset event UAPI structure
drivers/firewire/core-cdev.c | 63 +++++++++++++++++++++++-------------
drivers/firewire/uapi-test.c | 20 ++++++++++++
2 files changed, 60 insertions(+), 23 deletions(-)
base-commit: a20545fbf9226591017fc371bdfb1bfd490b0e0b
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/5] firewire: cdev: use memory barrier to access fw_device members
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
@ 2026-09-20 7:14 ` Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 2/5] firewire: cdev: refactor using scoped_guard() for get_info ioctl call Takashi Sakamoto
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:14 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
The core code uses a read memory barrier when accessing the generation
and node_id members of the fw_device structure. It assumes that the
latest value of the former is visible to a processor core when the latest
value of the latter is visible, since these members are updated in reverse
order with a write memory barrier.
However, the function that fills the fw_cdev_event_bus_reset structure
does not follow this approach. Use a read memory barrier when accessing
these members.
---
drivers/firewire/core-cdev.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index e49d8a58be09..9a3be1ceee2a 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -399,18 +399,21 @@ static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
struct client *client)
{
- struct fw_card *card = client->device->card;
-
- guard(spinlock_irq)(&card->lock);
-
event->closure = client->bus_reset_closure;
event->type = FW_CDEV_EVENT_BUS_RESET;
+
event->generation = client->device->generation;
+ smp_rmb();
event->node_id = client->device->node_id;
- event->local_node_id = card->local_node->node_id;
- event->bm_node_id = card->bm_node_id;
- event->irm_node_id = card->irm_node->node_id;
- event->root_node_id = card->root_node->node_id;
+
+ struct fw_card *card = client->device->card;
+
+ scoped_guard(spinlock_irq, &card->lock) {
+ event->local_node_id = card->local_node->node_id;
+ event->bm_node_id = card->bm_node_id;
+ event->irm_node_id = card->irm_node->node_id;
+ event->root_node_id = card->root_node->node_id;
+ }
}
static void for_each_client(struct fw_device *device,
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/5] firewire: cdev: refactor using scoped_guard() for get_info ioctl call
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 1/5] firewire: cdev: use memory barrier to access fw_device members Takashi Sakamoto
@ 2026-09-20 7:14 ` Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 3/5] firewire: cdev: refactor copy_to_user() in " Takashi Sakamoto
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:14 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
The scope of scoped_guard() corresponds to the code block, making it
easier to understand which code is protected by the locking primitive.
---
drivers/firewire/core-cdev.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 9a3be1ceee2a..bdc16207d743 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -491,7 +491,6 @@ union ioctl_arg {
static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
{
struct fw_cdev_get_info *a = &arg->get_info;
- struct fw_cdev_event_bus_reset bus_reset;
unsigned long ret = 0;
client->version = a->version;
@@ -511,16 +510,21 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
a->rom_length = client->device->config_rom_length * 4;
}
- guard(mutex)(&client->device->client_list_mutex);
+ scoped_guard(mutex, &client->device->client_list_mutex) {
+ client->bus_reset_closure = a->bus_reset_closure;
- client->bus_reset_closure = a->bus_reset_closure;
- if (a->bus_reset != 0) {
- fill_bus_reset_event(&bus_reset, client);
- /* unaligned size of bus_reset is 36 bytes */
- ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
+ if (a->bus_reset != 0) {
+ struct fw_cdev_event_bus_reset bus_reset;
+
+ memset(&bus_reset, 0, sizeof(bus_reset));
+ fill_bus_reset_event(&bus_reset, client);
+
+ /* unaligned size of bus_reset is 36 bytes */
+ ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
+ }
+ if (ret == 0 && list_empty(&client->link))
+ list_add_tail(&client->link, &client->device->client_list);
}
- if (ret == 0 && list_empty(&client->link))
- list_add_tail(&client->link, &client->device->client_list);
return ret ? -EFAULT : 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/5] firewire: cdev: refactor copy_to_user() in get_info ioctl call
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 1/5] firewire: cdev: use memory barrier to access fw_device members Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 2/5] firewire: cdev: refactor using scoped_guard() for get_info ioctl call Takashi Sakamoto
@ 2026-09-20 7:14 ` Takashi Sakamoto
2026-09-20 7:15 ` [PATCH 4/5] firewire: cdev: add annotations and comments about race condition of bus_reset_closure Takashi Sakamoto
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:14 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
A non-zero return value from copy_to_user() is handled by returning
-EFAULT. Using scoped_guard() simplifies this handling, since it
eliminates the need for a local variable to store the return value and
check it after the locking primitive is released.
Refactor the code to remove the local variable.
---
drivers/firewire/core-cdev.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index bdc16207d743..25590580b993 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -491,7 +491,6 @@ union ioctl_arg {
static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
{
struct fw_cdev_get_info *a = &arg->get_info;
- unsigned long ret = 0;
client->version = a->version;
a->version = FW_CDEV_KERNEL_VERSION;
@@ -499,12 +498,10 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
scoped_guard(rwsem_read, &fw_device_rwsem) {
if (a->rom != 0) {
- size_t want = a->rom_length;
- size_t have = client->device->config_rom_length * 4;
+ size_t length = min_t(size_t, a->rom_length,
+ client->device->config_rom_length * 4);
- ret = copy_to_user(u64_to_uptr(a->rom), client->device->config_rom,
- min(want, have));
- if (ret != 0)
+ if (copy_to_user(u64_to_uptr(a->rom), client->device->config_rom, length))
return -EFAULT;
}
a->rom_length = client->device->config_rom_length * 4;
@@ -520,13 +517,15 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
fill_bus_reset_event(&bus_reset, client);
/* unaligned size of bus_reset is 36 bytes */
- ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
+ if (copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36))
+ return -EFAULT;
}
- if (ret == 0 && list_empty(&client->link))
+
+ if (list_empty(&client->link))
list_add_tail(&client->link, &client->device->client_list);
}
- return ret ? -EFAULT : 0;
+ return 0;
}
static int add_client_resource(struct client *client, struct client_resource *resource,
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/5] firewire: cdev: add annotations and comments about race condition of bus_reset_closure
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
` (2 preceding siblings ...)
2026-09-20 7:14 ` [PATCH 3/5] firewire: cdev: refactor copy_to_user() in " Takashi Sakamoto
@ 2026-09-20 7:15 ` Takashi Sakamoto
2026-09-20 7:15 ` [PATCH 5/5] firewire: cdev: add KUnit test for bus reset event UAPI structure Takashi Sakamoto
2026-09-21 2:32 ` [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:15 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
There is a race condition involving the bus_reset_closure member of the
fw_cdev_event_bus_reset structure, which was addressed by commit
93b37905f70 ("firewire: cdev: prevent race between first get_info ioctl
and bus reset event queuing").
Add code comments and annotations to document it.
---
drivers/firewire/core-cdev.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 25590580b993..50c675113d5c 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -398,7 +398,12 @@ static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
struct client *client)
+__must_hold(&client->device->client_list_mutex)
{
+ lockdep_assert_held(&client->device->client_list_mutex);
+
+ // This member is related to the above mutex. In detail, see 93b37905f70 ("firewire: cdev:
+ // prevent race between first get_info ioctl and bus reset event queuing").
event->closure = client->bus_reset_closure;
event->type = FW_CDEV_EVENT_BUS_RESET;
@@ -508,6 +513,9 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
}
scoped_guard(mutex, &client->device->client_list_mutex) {
+ // Coordinate concurrent access to this member with bus reset event handling, see
+ // 93b37905f70 ("firewire: cdev: prevent race between first get_info ioctl and bus
+ // reset event queuing").
client->bus_reset_closure = a->bus_reset_closure;
if (a->bus_reset != 0) {
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 5/5] firewire: cdev: add KUnit test for bus reset event UAPI structure
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
` (3 preceding siblings ...)
2026-09-20 7:15 ` [PATCH 4/5] firewire: cdev: add annotations and comments about race condition of bus_reset_closure Takashi Sakamoto
@ 2026-09-20 7:15 ` Takashi Sakamoto
2026-09-21 2:32 ` [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-20 7:15 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
The bus reset event structure exposed through the UAPI has a different
size depending on the target architecture. Extra care is required when
copying it to/from userspace.
Add a KUnit test for the structure to ensure that it has the expected
size and offsets.
---
drivers/firewire/core-cdev.c | 5 ++++-
drivers/firewire/uapi-test.c | 20 ++++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 50c675113d5c..6d91a6e7ad0d 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -524,7 +524,10 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
memset(&bus_reset, 0, sizeof(bus_reset));
fill_bus_reset_event(&bus_reset, client);
- /* unaligned size of bus_reset is 36 bytes */
+ // This structure has 4 bytes of trailing padding under the System V ABI
+ // on most architectures (due to 8-byte alignment of the long long type),
+ // except for Intel386 (where long long type is aligned to 4 bytes). In
+ // either case, the effective length is 36 bytes.
if (copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36))
return -EFAULT;
}
diff --git a/drivers/firewire/uapi-test.c b/drivers/firewire/uapi-test.c
index bc3f10a2e516..0bdacb1caeb8 100644
--- a/drivers/firewire/uapi-test.c
+++ b/drivers/firewire/uapi-test.c
@@ -72,11 +72,31 @@ static void structure_layout_event_phy_packet2(struct kunit *test)
KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_phy_packet2, data));
}
+static void structure_layout_event_bus_reset(struct kunit *test)
+{
+#if defined(CONFIG_X86_32)
+ // 4 bytes alignment for aggregate type including 8 bytes storage types.
+ KUNIT_EXPECT_EQ(test, 36, sizeof(struct fw_cdev_event_bus_reset));
+#else
+ KUNIT_EXPECT_EQ(test, 40, sizeof(struct fw_cdev_event_bus_reset));
+#endif
+
+ KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_bus_reset, closure));
+ KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_bus_reset, type));
+ KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_bus_reset, node_id));
+ KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_bus_reset, local_node_id));
+ KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_bus_reset, bm_node_id));
+ KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_bus_reset, irm_node_id));
+ KUNIT_EXPECT_EQ(test, 28, offsetof(struct fw_cdev_event_bus_reset, root_node_id));
+ KUNIT_EXPECT_EQ(test, 32, offsetof(struct fw_cdev_event_bus_reset, generation));
+}
+
static struct kunit_case structure_layout_test_cases[] = {
KUNIT_CASE(structure_layout_event_response),
KUNIT_CASE(structure_layout_event_request3),
KUNIT_CASE(structure_layout_event_response2),
KUNIT_CASE(structure_layout_event_phy_packet2),
+ KUNIT_CASE(structure_layout_event_bus_reset),
{}
};
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 0/5] firewire: cdev: refactor for bus reset event filling
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
` (4 preceding siblings ...)
2026-09-20 7:15 ` [PATCH 5/5] firewire: cdev: add KUnit test for bus reset event UAPI structure Takashi Sakamoto
@ 2026-09-21 2:32 ` Takashi Sakamoto
5 siblings, 0 replies; 7+ messages in thread
From: Takashi Sakamoto @ 2026-09-21 2:32 UTC (permalink / raw)
To: linux1394-devel; +Cc: linux-kernel
On Sun, Sep 20, 2026 at 04:14:56PM +0900, Takashi Sakamoto wrote:
> Hi,
>
> In the cdev layer, there are two places where bus reset events are filed;
> the update path for the associated fw_device instance and the get_info
> ioctl call. There is still some code that can be refactored in these
> paths.
>
> This series refactors the code to make the memory ordering and locking
> scope more explicit, and adds a KUnit test for the bus reset event UAPI
> structure.
>
> Takashi Sakamoto (5):
> firewire: cdev: use memory barrier to access fw_device members
> firewire: cdev: refactor using scoped_guard() for get_info ioctl call
> firewire: cdev: refactor copy_to_user() in get_info ioctl call
> firewire: cdev: add annotations and comments about race condition of
> bus_reset_closure
> firewire: cdev: add KUnit test for bus reset event UAPI structure
>
> drivers/firewire/core-cdev.c | 63 +++++++++++++++++++++++-------------
> drivers/firewire/uapi-test.c | 20 ++++++++++++
> 2 files changed, 60 insertions(+), 23 deletions(-)
Applied to for-next branch.
Regards
Takashi Sakamoto
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-21 2:32 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-20 7:14 [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 1/5] firewire: cdev: use memory barrier to access fw_device members Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 2/5] firewire: cdev: refactor using scoped_guard() for get_info ioctl call Takashi Sakamoto
2026-09-20 7:14 ` [PATCH 3/5] firewire: cdev: refactor copy_to_user() in " Takashi Sakamoto
2026-09-20 7:15 ` [PATCH 4/5] firewire: cdev: add annotations and comments about race condition of bus_reset_closure Takashi Sakamoto
2026-09-20 7:15 ` [PATCH 5/5] firewire: cdev: add KUnit test for bus reset event UAPI structure Takashi Sakamoto
2026-09-21 2:32 ` [PATCH 0/5] firewire: cdev: refactor for bus reset event filling Takashi Sakamoto
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®