From e05a76ae1507d19d62eb0011592be9efb42e06cd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:42 +0900 Subject: [PATCH 01/17] firewire: core: code refactoring for early return at client resource allocation The add_client_resource() function returns zero at success or negative value at error. The critical section is already protected by scoped_guard() macro. In this case, the programming pattern of early return improves code readability. Link: https://lore.kernel.org/r/20260429093449.160545-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f791db4c8dff..144625c34be2 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -507,31 +507,30 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) static int add_client_resource(struct client *client, struct client_resource *resource, gfp_t gfp_mask) { - int ret; - scoped_guard(spinlock_irqsave, &client->lock) { u32 index; + int ret; - if (client->in_shutdown) { - ret = -ECANCELED; + if (client->in_shutdown) + return -ECANCELED; + + if (gfpflags_allow_blocking(gfp_mask)) { + ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b, + GFP_NOWAIT); } else { - if (gfpflags_allow_blocking(gfp_mask)) { - ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b, - GFP_NOWAIT); - } else { - ret = xa_alloc_bh(&client->resource_xa, &index, resource, - xa_limit_32b, GFP_NOWAIT); - } - } - if (ret >= 0) { - resource->handle = index; - client_get(client); - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); + ret = xa_alloc_bh(&client->resource_xa, &index, resource, + xa_limit_32b, GFP_NOWAIT); } + if (ret < 0) + return ret; + + resource->handle = index; + client_get(client); + if (is_iso_resource(resource)) + schedule_iso_resource(to_iso_resource(resource), 0); } - return ret < 0 ? ret : 0; + return 0; } static int release_client_resource(struct client *client, u32 handle, From 38fb1154185dfdd8ac2162da1da688f16ed66b9b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:43 +0900 Subject: [PATCH 02/17] firewire: core: code refactoring to queue work item for iso_resource The add_client_resource() function checks the type of client resource every time to be called. If the type is for iso_resource, it schedules work item. However, the iso_resource client resource is only added by the call of init_iso_resource(). There is no need to check the type every time adding any client resource. Link: https://lore.kernel.org/r/20260429093449.160545-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 144625c34be2..8391c7efab2c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -526,8 +526,6 @@ static int add_client_resource(struct client *client, struct client_resource *re resource->handle = index; client_get(client); - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); } return 0; @@ -1438,8 +1436,9 @@ static int init_iso_resource(struct client *client, } else { r->resource.release = NULL; r->resource.handle = -1; - schedule_iso_resource(r, 0); } + schedule_iso_resource(r, 0); + request->handle = r->resource.handle; return 0; From e698cec3117fb26fc2550cd0858174484dd90cc2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:44 +0900 Subject: [PATCH 03/17] firewire: core: code refactoring for helper function to fill iso_resource parameters This change is a preparation for future changes. The added helper function will be reused in the changes to fill iso_resource parameters according to the users' request. Link: https://lore.kernel.org/r/20260429093449.160545-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 45 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 8391c7efab2c..effa03739679 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -128,6 +128,12 @@ struct descriptor_resource { u32 data[]; }; +struct iso_resource_params { + int generation; + u64 channels; + s32 bandwidth; +}; + struct iso_resource { struct client_resource resource; struct client *client; @@ -135,9 +141,7 @@ struct iso_resource { struct delayed_work work; enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; - int generation; - u64 channels; - s32 bandwidth; + struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1290,6 +1294,20 @@ static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) return 0; } +static int fill_iso_resource_params(struct iso_resource_params *params, + struct fw_cdev_allocate_iso_resource *request) +{ + if ((request->channels == 0 && request->bandwidth == 0) || + request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) + return -EINVAL; + + params->generation = -1; + params->channels = request->channels; + params->bandwidth = request->bandwidth; + + return 0; +} + static void iso_resource_work(struct work_struct *work) { struct iso_resource_event *e; @@ -1310,21 +1328,21 @@ static void iso_resource_work(struct work_struct *work) } else { // We could be called twice within the same generation. skip = todo == ISO_RES_REALLOC && - r->generation == generation; + r->params.generation == generation; } free = todo == ISO_RES_DEALLOC || todo == ISO_RES_ALLOC_ONCE || todo == ISO_RES_DEALLOC_ONCE; - r->generation = generation; + r->params.generation = generation; } if (skip) goto out; - bandwidth = r->bandwidth; + bandwidth = r->params.bandwidth; fw_iso_resource_manage(client->device->card, generation, - r->channels, &channel, &bandwidth, + r->params.channels, &channel, &bandwidth, todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC || todo == ISO_RES_ALLOC_ONCE); @@ -1355,7 +1373,7 @@ static void iso_resource_work(struct work_struct *work) } if (todo == ISO_RES_ALLOC && channel >= 0) - r->channels = 1ULL << channel; + r->params.channels = 1ULL << channel; if (todo == ISO_RES_REALLOC && success) goto out; @@ -1402,10 +1420,6 @@ static int init_iso_resource(struct client *client, struct iso_resource *r; int ret; - if ((request->channels == 0 && request->bandwidth == 0) || - request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) - return -EINVAL; - r = kmalloc_obj(*r); e1 = kmalloc_obj(*e1); e2 = kmalloc_obj(*e2); @@ -1414,12 +1428,13 @@ static int init_iso_resource(struct client *client, goto fail; } + ret = fill_iso_resource_params(&r->params, request); + if (ret < 0) + goto fail; + INIT_DELAYED_WORK(&r->work, iso_resource_work); r->client = client; r->todo = todo; - r->generation = -1; - r->channels = request->channels; - r->bandwidth = request->bandwidth; r->e_alloc = e1; r->e_dealloc = e2; From b3ac3b453b23423e2c713d9ac497ddb0aec9aa7c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:45 +0900 Subject: [PATCH 04/17] firewire: core: split functions for iso_resource once operation Unlike FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE operation, the operations of FW_CDEV_IOC_[DE]ALLOCATE_ISO_RESOURCE_ONCE require no client resource, thus they keeps no handle value. This commit adds the series of functions to separate these operations, according to divide-and-conquer methodology. Link: https://lore.kernel.org/r/20260429093449.160545-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 83 ++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index effa03739679..478e8f6400f0 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -145,6 +145,18 @@ struct iso_resource { struct iso_resource_event *e_alloc, *e_dealloc; }; +struct iso_resource_once { + struct client *client; + // Schedule work and access todo only with client->lock held. + struct delayed_work work; + enum { + ISO_RES_ONCE_ALLOC, + ISO_RES_ONCE_DEALLOC, + } todo; + struct iso_resource_params params; + struct iso_resource_event *event; +}; + static struct address_handler_resource *to_address_handler_resource(struct client_resource *resource) { return container_of(resource, struct address_handler_resource, resource); @@ -1479,18 +1491,81 @@ static int ioctl_deallocate_iso_resource(struct client *client, arg->deallocate.handle, release_iso_resource, NULL); } +#define UNAVAILABLE_HANDLE -1 + +static void iso_resource_once_work(struct work_struct *work) +{ + struct iso_resource_once *r = from_work(r, work, work.work); + struct client *client = r->client; + struct iso_resource_event *e = r->event; + int generation, channel, bandwidth; + + scoped_guard(spinlock_irq, &client->lock) + generation = client->device->generation; + + r->params.generation = generation; + bandwidth = r->params.bandwidth; + + fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, + &bandwidth, r->todo == ISO_RES_ONCE_ALLOC); + + e->iso_resource.handle = UNAVAILABLE_HANDLE; + e->iso_resource.channel = channel; + e->iso_resource.bandwidth = bandwidth; + + queue_event(client, &e->event, &e->iso_resource, sizeof(e->iso_resource), NULL, 0); + + cancel_delayed_work(&r->work); + kfree(r); + + client_put(client); +} + +static int init_iso_resource_once(struct client *client, + struct fw_cdev_allocate_iso_resource *request, int todo) +{ + struct iso_resource_event *e __free(kfree) = kmalloc_obj(*e); + struct iso_resource_once *r __free(kfree) = kmalloc_obj(*r); + int err; + + if (!r || !e) + return -ENOMEM; + + err = fill_iso_resource_params(&r->params, request); + if (err < 0) + return err; + + INIT_DELAYED_WORK(&r->work, iso_resource_once_work); + r->client = client; + r->todo = todo; + + if (todo == ISO_RES_ONCE_ALLOC) + e->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; + else + e->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; + e->iso_resource.closure = request->closure; + r->event = no_free_ptr(e); + + // Keep the client until work item finishing. + client_get(r->client); + + queue_delayed_work(fw_workqueue, &no_free_ptr(r)->work, 0); + + request->handle = UNAVAILABLE_HANDLE; + + return 0; +} + static int ioctl_allocate_iso_resource_once(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); + return init_iso_resource_once(client, &arg->allocate_iso_resource, ISO_RES_ONCE_ALLOC); } static int ioctl_deallocate_iso_resource_once(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); + return init_iso_resource_once(client, &arg->allocate_iso_resource, ISO_RES_ONCE_DEALLOC); } /* From cd5f1a1126eeb2f6bd53d45684233e95dff41d82 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:46 +0900 Subject: [PATCH 05/17] firewire: core: code cleanup to remove old implementations for once operation The helper functions for iso_resource allocation and work item still include codes for once operation. This commit refactors them to remove the old implementations. Link: https://lore.kernel.org/r/20260429093449.160545-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 478e8f6400f0..f81a8aa4bcbc 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -139,8 +139,11 @@ struct iso_resource { struct client *client; /* Schedule work and access todo only with client->lock held. */ struct delayed_work work; - enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, - ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; + enum { + ISO_RES_ALLOC, + ISO_RES_REALLOC, + ISO_RES_DEALLOC, + } todo; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1342,9 +1345,7 @@ static void iso_resource_work(struct work_struct *work) skip = todo == ISO_RES_REALLOC && r->params.generation == generation; } - free = todo == ISO_RES_DEALLOC || - todo == ISO_RES_ALLOC_ONCE || - todo == ISO_RES_DEALLOC_ONCE; + free = todo == ISO_RES_DEALLOC; r->params.generation = generation; } @@ -1356,8 +1357,7 @@ static void iso_resource_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, &bandwidth, todo == ISO_RES_ALLOC || - todo == ISO_RES_REALLOC || - todo == ISO_RES_ALLOC_ONCE); + todo == ISO_RES_REALLOC); /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at @@ -1390,7 +1390,7 @@ static void iso_resource_work(struct work_struct *work) if (todo == ISO_RES_REALLOC && success) goto out; - if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { + if (todo == ISO_RES_ALLOC) { e = r->e_alloc; r->e_alloc = NULL; } else { @@ -1425,8 +1425,7 @@ static void release_iso_resource(struct client *client, schedule_iso_resource(r, 0); } -static int init_iso_resource(struct client *client, - struct fw_cdev_allocate_iso_resource *request, int todo) +static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) { struct iso_resource_event *e1, *e2; struct iso_resource *r; @@ -1446,7 +1445,7 @@ static int init_iso_resource(struct client *client, INIT_DELAYED_WORK(&r->work, iso_resource_work); r->client = client; - r->todo = todo; + r->todo = ISO_RES_ALLOC; r->e_alloc = e1; r->e_dealloc = e2; @@ -1455,15 +1454,10 @@ static int init_iso_resource(struct client *client, e2->iso_resource.closure = request->closure; e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; - if (todo == ISO_RES_ALLOC) { - r->resource.release = release_iso_resource; - ret = add_client_resource(client, &r->resource, GFP_KERNEL); - if (ret < 0) - goto fail; - } else { - r->resource.release = NULL; - r->resource.handle = -1; - } + r->resource.release = release_iso_resource; + ret = add_client_resource(client, &r->resource, GFP_KERNEL); + if (ret < 0) + goto fail; schedule_iso_resource(r, 0); request->handle = r->resource.handle; @@ -1480,8 +1474,7 @@ static int init_iso_resource(struct client *client, static int ioctl_allocate_iso_resource(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_ALLOC); + return init_iso_resource(client, &arg->allocate_iso_resource); } static int ioctl_deallocate_iso_resource(struct client *client, From 48b68337bf6523f16b2afbb0d3e059eb211e3c85 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:47 +0900 Subject: [PATCH 06/17] firewire: core: append _auto suffix for non-once iso resource operations The functions for iso_resource once operations are carefully split from another type of operation. This commit adds _auto suffix to functions for the another type so that it is easily to distinguish them. Link: https://lore.kernel.org/r/20260429093449.160545-7-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 75 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f81a8aa4bcbc..b3ce34d777c3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -134,15 +134,15 @@ struct iso_resource_params { s32 bandwidth; }; -struct iso_resource { +struct iso_resource_auto { struct client_resource resource; struct client *client; /* Schedule work and access todo only with client->lock held. */ struct delayed_work work; enum { - ISO_RES_ALLOC, - ISO_RES_REALLOC, - ISO_RES_DEALLOC, + ISO_RES_AUTO_ALLOC, + ISO_RES_AUTO_REALLOC, + ISO_RES_AUTO_DEALLOC, } todo; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; @@ -175,16 +175,16 @@ static struct descriptor_resource *to_descriptor_resource(struct client_resource return container_of(resource, struct descriptor_resource, resource); } -static struct iso_resource *to_iso_resource(struct client_resource *resource) +static struct iso_resource_auto *to_iso_resource_auto(struct client_resource *resource) { - return container_of(resource, struct iso_resource, resource); + return container_of(resource, struct iso_resource_auto, resource); } -static void release_iso_resource(struct client *, struct client_resource *); +static void release_iso_resource_auto(struct client *, struct client_resource *); -static int is_iso_resource(const struct client_resource *resource) +static int is_iso_resource_auto(const struct client_resource *resource) { - return resource->release == release_iso_resource; + return resource->release == release_iso_resource_auto; } static void release_transaction(struct client *client, @@ -195,7 +195,7 @@ static int is_outbound_transaction_resource(const struct client_resource *resour return resource->release == release_transaction; } -static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) +static void schedule_iso_resource_auto(struct iso_resource_auto *r, unsigned long delay) { client_get(r->client); if (!queue_delayed_work(fw_workqueue, &r->work, delay)) @@ -443,8 +443,8 @@ static void queue_bus_reset_event(struct client *client) guard(spinlock_irq)(&client->lock); xa_for_each(&client->resource_xa, index, resource) { - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); + if (is_iso_resource_auto(resource)) + schedule_iso_resource_auto(to_iso_resource_auto(resource), 0); } } @@ -1323,10 +1323,10 @@ static int fill_iso_resource_params(struct iso_resource_params *params, return 0; } -static void iso_resource_work(struct work_struct *work) +static void iso_resource_auto_work(struct work_struct *work) { struct iso_resource_event *e; - struct iso_resource *r = from_work(r, work, work.work); + struct iso_resource_auto *r = from_work(r, work, work.work); struct client *client = r->client; unsigned long index = r->resource.handle; int generation, channel, bandwidth, todo; @@ -1336,16 +1336,16 @@ static void iso_resource_work(struct work_struct *work) generation = client->device->generation; todo = r->todo; // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_ALLOC && + if (todo == ISO_RES_AUTO_ALLOC && time_is_after_jiffies64(client->device->card->reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource(r, msecs_to_jiffies(333)); + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); skip = true; } else { // We could be called twice within the same generation. - skip = todo == ISO_RES_REALLOC && + skip = todo == ISO_RES_AUTO_REALLOC && r->params.generation == generation; } - free = todo == ISO_RES_DEALLOC; + free = todo == ISO_RES_AUTO_DEALLOC; r->params.generation = generation; } @@ -1356,15 +1356,15 @@ static void iso_resource_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, &bandwidth, - todo == ISO_RES_ALLOC || - todo == ISO_RES_REALLOC); + todo == ISO_RES_AUTO_ALLOC || + todo == ISO_RES_AUTO_REALLOC); /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at * shutdown. */ if (channel == -EAGAIN && - (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) + (todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC)) goto out; success = channel >= 0 || bandwidth > 0; @@ -1372,11 +1372,11 @@ static void iso_resource_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) { // Transit from allocation to reallocation, except if the client // requested deallocation in the meantime. - if (r->todo == ISO_RES_ALLOC) - r->todo = ISO_RES_REALLOC; + if (r->todo == ISO_RES_AUTO_ALLOC) + r->todo = ISO_RES_AUTO_REALLOC; // Allocation or reallocation failure? Pull this resource out of the // xarray and prepare for deletion, unless the client is shutting down. - if (r->todo == ISO_RES_REALLOC && !success && + if (r->todo == ISO_RES_AUTO_REALLOC && !success && !client->in_shutdown && xa_erase(&client->resource_xa, index)) { client_put(client); @@ -1384,13 +1384,13 @@ static void iso_resource_work(struct work_struct *work) } } - if (todo == ISO_RES_ALLOC && channel >= 0) + if (todo == ISO_RES_AUTO_ALLOC && channel >= 0) r->params.channels = 1ULL << channel; - if (todo == ISO_RES_REALLOC && success) + if (todo == ISO_RES_AUTO_REALLOC && success) goto out; - if (todo == ISO_RES_ALLOC) { + if (todo == ISO_RES_AUTO_ALLOC) { e = r->e_alloc; r->e_alloc = NULL; } else { @@ -1414,21 +1414,20 @@ static void iso_resource_work(struct work_struct *work) client_put(client); } -static void release_iso_resource(struct client *client, - struct client_resource *resource) +static void release_iso_resource_auto(struct client *client, struct client_resource *resource) { - struct iso_resource *r = to_iso_resource(resource); + struct iso_resource_auto *r = to_iso_resource_auto(resource); guard(spinlock_irq)(&client->lock); - r->todo = ISO_RES_DEALLOC; - schedule_iso_resource(r, 0); + r->todo = ISO_RES_AUTO_DEALLOC; + schedule_iso_resource_auto(r, 0); } static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) { struct iso_resource_event *e1, *e2; - struct iso_resource *r; + struct iso_resource_auto *r; int ret; r = kmalloc_obj(*r); @@ -1443,9 +1442,9 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ if (ret < 0) goto fail; - INIT_DELAYED_WORK(&r->work, iso_resource_work); + INIT_DELAYED_WORK(&r->work, iso_resource_auto_work); r->client = client; - r->todo = ISO_RES_ALLOC; + r->todo = ISO_RES_AUTO_ALLOC; r->e_alloc = e1; r->e_dealloc = e2; @@ -1454,11 +1453,11 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ e2->iso_resource.closure = request->closure; e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; - r->resource.release = release_iso_resource; + r->resource.release = release_iso_resource_auto; ret = add_client_resource(client, &r->resource, GFP_KERNEL); if (ret < 0) goto fail; - schedule_iso_resource(r, 0); + schedule_iso_resource_auto(r, 0); request->handle = r->resource.handle; @@ -1481,7 +1480,7 @@ static int ioctl_deallocate_iso_resource(struct client *client, union ioctl_arg *arg) { return release_client_resource(client, - arg->deallocate.handle, release_iso_resource, NULL); + arg->deallocate.handle, release_iso_resource_auto, NULL); } #define UNAVAILABLE_HANDLE -1 From 6dbe7653fa01edeefc77b4d7c063562eb3debd48 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:48 +0900 Subject: [PATCH 07/17] firewire: core: code cleanup for iso resource auto creation The init_iso_resource function is only called by ioctl_allocate_iso_resource(), thus no need to be unique. This commit unifies them with minor code refactoring. Link: https://lore.kernel.org/r/20260429093449.160545-8-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 53 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index b3ce34d777c3..bcfb20b770df 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1424,23 +1424,20 @@ static void release_iso_resource_auto(struct client *client, struct client_resou schedule_iso_resource_auto(r, 0); } -static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) +static int ioctl_allocate_iso_resource(struct client *client, union ioctl_arg *arg) { - struct iso_resource_event *e1, *e2; - struct iso_resource_auto *r; - int ret; + struct fw_cdev_allocate_iso_resource *request = &arg->allocate_iso_resource; + struct iso_resource_event *e1 __free(kfree) = kmalloc_obj(*e1); + struct iso_resource_event *e2 __free(kfree) = kmalloc_obj(*e2); + struct iso_resource_auto *r __free(kfree) = kmalloc_obj(*r); + int err; - r = kmalloc_obj(*r); - e1 = kmalloc_obj(*e1); - e2 = kmalloc_obj(*e2); - if (r == NULL || e1 == NULL || e2 == NULL) { - ret = -ENOMEM; - goto fail; - } + if (!r || !e1 || !e2) + return -ENOMEM; - ret = fill_iso_resource_params(&r->params, request); - if (ret < 0) - goto fail; + err = fill_iso_resource_params(&r->params, request); + if (err < 0) + return err; INIT_DELAYED_WORK(&r->work, iso_resource_auto_work); r->client = client; @@ -1449,31 +1446,21 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ r->e_dealloc = e2; e1->iso_resource.closure = request->closure; - e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; + e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; e2->iso_resource.closure = request->closure; - e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; + e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; r->resource.release = release_iso_resource_auto; - ret = add_client_resource(client, &r->resource, GFP_KERNEL); - if (ret < 0) - goto fail; - schedule_iso_resource_auto(r, 0); - + err = add_client_resource(client, &r->resource, GFP_KERNEL); + if (err < 0) + return err; request->handle = r->resource.handle; + retain_and_null_ptr(e1); + retain_and_null_ptr(e2); + schedule_iso_resource_auto(no_free_ptr(r), 0); + return 0; - fail: - kfree(r); - kfree(e1); - kfree(e2); - - return ret; -} - -static int ioctl_allocate_iso_resource(struct client *client, - union ioctl_arg *arg) -{ - return init_iso_resource(client, &arg->allocate_iso_resource); } static int ioctl_deallocate_iso_resource(struct client *client, From 834b33f9c95174606b25848b43b32432a3e98713 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:20 +0900 Subject: [PATCH 08/17] firewire: core: reduce critical section duration in pre-processing of isoc resource management in cdev It is preferable for the critical section to be as small as possible. Current implementation of iso_resource_auto_work() function uses a spinlock to control concurrent access to members of fw_card, fw_device, iso_resource_auto structures, however the locking duration could be reduced. This commit refactors to shorten that duration. Link: https://lore.kernel.org/r/20260501135823.241940-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index bcfb20b770df..887783e4bd52 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1329,32 +1329,36 @@ static void iso_resource_auto_work(struct work_struct *work) struct iso_resource_auto *r = from_work(r, work, work.work); struct client *client = r->client; unsigned long index = r->resource.handle; - int generation, channel, bandwidth, todo; + int current_generation, resource_generation, channel, bandwidth, todo; + u64 reset_jiffies; bool skip, free, success; scoped_guard(spinlock_irq, &client->lock) { - generation = client->device->generation; + reset_jiffies = client->device->card->reset_jiffies; + current_generation = client->device->generation; + resource_generation = r->params.generation; + r->params.generation = current_generation; todo = r->todo; - // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_AUTO_ALLOC && - time_is_after_jiffies64(client->device->card->reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource_auto(r, msecs_to_jiffies(333)); - skip = true; - } else { - // We could be called twice within the same generation. - skip = todo == ISO_RES_AUTO_REALLOC && - r->params.generation == generation; - } - free = todo == ISO_RES_AUTO_DEALLOC; - r->params.generation = generation; } + // Allow 1000ms grace period for other reallocations. + if (todo == ISO_RES_AUTO_ALLOC && + time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); + skip = true; + } else { + // We could be called twice within the same generation. + skip = todo == ISO_RES_AUTO_REALLOC && + resource_generation == current_generation; + } + free = todo == ISO_RES_AUTO_DEALLOC; + if (skip) goto out; bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, generation, + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, &channel, &bandwidth, todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC); From 92750bccf01f2e65976429acee06984a95803354 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:21 +0900 Subject: [PATCH 09/17] firewire: core: use switch statement for post-processing of isoc resource management in cdev The iso_resource_auto structure object has three states. The current implementation of state evaluation before managing the actual isochronous resources can be improved. This commit refactors the evaluation logic using a switch statement. Link: https://lore.kernel.org/r/20260501135823.241940-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 37 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 887783e4bd52..0d57b61ade12 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1331,7 +1331,7 @@ static void iso_resource_auto_work(struct work_struct *work) unsigned long index = r->resource.handle; int current_generation, resource_generation, channel, bandwidth, todo; u64 reset_jiffies; - bool skip, free, success; + bool free = false, success; scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; @@ -1341,27 +1341,29 @@ static void iso_resource_auto_work(struct work_struct *work) todo = r->todo; } - // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_AUTO_ALLOC && - time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource_auto(r, msecs_to_jiffies(333)); - skip = true; - } else { + switch (todo) { + case ISO_RES_AUTO_ALLOC: + // Allow 1000ms grace period for other reallocations. + if (time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); + goto out; + } + break; + case ISO_RES_AUTO_REALLOC: // We could be called twice within the same generation. - skip = todo == ISO_RES_AUTO_REALLOC && - resource_generation == current_generation; + if (resource_generation == current_generation) + goto out; + break; + case ISO_RES_AUTO_DEALLOC: + default: + break; } - free = todo == ISO_RES_AUTO_DEALLOC; - - if (skip) - goto out; bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, current_generation, - r->params.channels, &channel, &bandwidth, - todo == ISO_RES_AUTO_ALLOC || - todo == ISO_RES_AUTO_REALLOC); + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, + &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); + /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at @@ -1398,6 +1400,7 @@ static void iso_resource_auto_work(struct work_struct *work) e = r->e_alloc; r->e_alloc = NULL; } else { + free = true; e = r->e_dealloc; r->e_dealloc = NULL; } From afa66eeb1899087b205f49cdfb8465880d61cdd2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:22 +0900 Subject: [PATCH 10/17] firewire: core: refactor notification type determination after isoc resource management in cdev After managing the actual isochronous resources, there is post-processing logic to determine what type of event should be notified. However, there is room for improvement. This commit refactors the logic. Link: https://lore.kernel.org/r/20260501135823.241940-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 0d57b61ade12..4ce8754da93f 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1390,20 +1390,27 @@ static void iso_resource_auto_work(struct work_struct *work) } } - if (todo == ISO_RES_AUTO_ALLOC && channel >= 0) - r->params.channels = 1ULL << channel; - - if (todo == ISO_RES_AUTO_REALLOC && success) - goto out; - - if (todo == ISO_RES_AUTO_ALLOC) { - e = r->e_alloc; - r->e_alloc = NULL; - } else { + if (todo == ISO_RES_AUTO_DEALLOC) { free = true; e = r->e_dealloc; r->e_dealloc = NULL; + } else { + if (todo == ISO_RES_AUTO_REALLOC) { + if (success) + goto out; + + // Notify the userspace client of the failure through a deallocation event. + e = r->e_dealloc; + r->e_dealloc = NULL; + } else { + if (channel >= 0) + r->params.channels = 1ULL << channel; + + e = r->e_alloc; + r->e_alloc = NULL; + } } + e->iso_resource.handle = r->resource.handle; e->iso_resource.channel = channel; e->iso_resource.bandwidth = bandwidth; From fcabbf40fae501379b4f3a057febe92d4b0ebdd8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:23 +0900 Subject: [PATCH 11/17] firewire: core: move allocation/reallocation paths into specific branch after isoc resource management in cdev After managing the actual isochronous resources, there is post-processing logic to determine what type of event should be notified. However, there is room for improvement. This commit refactors the logic. Link: https://lore.kernel.org/r/20260501135823.241940-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 53 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4ce8754da93f..c166e7617d2a 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1331,7 +1331,7 @@ static void iso_resource_auto_work(struct work_struct *work) unsigned long index = r->resource.handle; int current_generation, resource_generation, channel, bandwidth, todo; u64 reset_jiffies; - bool free = false, success; + bool free; scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; @@ -1364,37 +1364,31 @@ static void iso_resource_auto_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); - /* - * Is this generation outdated already? As long as this resource sticks - * in the xarray, it will be scheduled again for a newer generation or at - * shutdown. - */ - if (channel == -EAGAIN && - (todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC)) - goto out; - - success = channel >= 0 || bandwidth > 0; - - scoped_guard(spinlock_irq, &client->lock) { - // Transit from allocation to reallocation, except if the client - // requested deallocation in the meantime. - if (r->todo == ISO_RES_AUTO_ALLOC) - r->todo = ISO_RES_AUTO_REALLOC; - // Allocation or reallocation failure? Pull this resource out of the - // xarray and prepare for deletion, unless the client is shutting down. - if (r->todo == ISO_RES_AUTO_REALLOC && !success && - !client->in_shutdown && - xa_erase(&client->resource_xa, index)) { - client_put(client); - free = true; - } - } - if (todo == ISO_RES_AUTO_DEALLOC) { free = true; e = r->e_dealloc; r->e_dealloc = NULL; } else { + free = false; + + // Is this generation outdated already? As long as this resource sticks in the + // xarray, it will be scheduled again for a newer generation or at shutdown. + if (channel == -EAGAIN) + goto out; + + bool success = channel >= 0 || bandwidth > 0; + + if (!success) { + // Allocation or reallocation failure? Pull this resource out of the + // xarray and prepare for deletion, unless the client is shutting down. + scoped_guard(spinlock_irq, &client->lock) { + if (!client->in_shutdown && xa_erase(&client->resource_xa, index)) { + client_put(client); + free = true; + } + } + } + if (todo == ISO_RES_AUTO_REALLOC) { if (success) goto out; @@ -1403,6 +1397,11 @@ static void iso_resource_auto_work(struct work_struct *work) e = r->e_dealloc; r->e_dealloc = NULL; } else { + // Transit from allocation to reallocation, except if the client requested + // deallocation in the meantime. + scoped_guard(spinlock_irq, &client->lock) + r->todo = ISO_RES_AUTO_REALLOC; + if (channel >= 0) r->params.channels = 1ULL << channel; From 39d260d6bc7aab6777c0d14d1e27648ca8e9252e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 11 May 2026 12:45:02 +0200 Subject: [PATCH 12/17] firewire: Simplify storing pointers in device id struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Technically it is fine (on all current Linux architectures) to store a pointer in an unsigned long variable. However this needs explicit casting which is an easy source for type mismatches. By replacing the plain unsigned long .driver_data in struct ieee1394_device_id by an anonymous union, most of the casting can be dropped. There is still some implicit casting involved (between a void * and a driver specific pointer type), but that's better than the approach to store a pointer in an unsigned long variable as this doesn't lose the information that the data being pointed to is const. All users of struct ieee1394_device_id are initialized in a way that is compatible with the new definition, so no adaptions are needed there. (The comments addressing to CHERI extension are dropped by the maintainer.) Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/e5ba45a7e386461c0b1a5001635aa008b01c2164.1778494204.git.u.kleine-koenig@baylibre.com Signed-off-by: Takashi Sakamoto --- include/linux/mod_devicetable.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 23ff24080dfd..3b0c9a251a2e 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -61,7 +61,10 @@ struct ieee1394_device_id { __u32 model_id; __u32 specifier_id; __u32 version; - kernel_ulong_t driver_data; + union { + kernel_ulong_t driver_data; + const void *driver_data_ptr; + }; }; From 8208d94f149a53311ac7687c051cb3a6d58063f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 11 May 2026 12:45:03 +0200 Subject: [PATCH 13/17] ALSA: firewire: Make use of ieee1394's .driver_data_ptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently struct ieee1394_device_id gained a new member to store a pointer to driver data. Make use of that to get rid of a bunch of casts. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/6b7b7b3b8b54465ad5e7463412a202350fccbb62.1778494204.git.u.kleine-koenig@baylibre.com Signed-off-by: Takashi Sakamoto --- sound/firewire/dice/dice.c | 34 +++++++++++++++++----------------- sound/firewire/fireface/ff.c | 12 ++++++------ sound/firewire/motu/motu.c | 6 +++--- sound/firewire/oxfw/oxfw.c | 4 ++-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index f7a50bae4b55..58f14aadc73d 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -148,7 +148,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent snd_dice_detect_formats_t detect_formats; int err; - if (!entry->driver_data && entry->vendor_id != OUI_SSL) { + if (!entry->driver_data_ptr && entry->vendor_id != OUI_SSL) { err = check_dice_category(unit); if (err < 0) return -ENODEV; @@ -164,10 +164,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent dev_set_drvdata(&unit->device, dice); dice->card = card; - if (!entry->driver_data) + if (!entry->driver_data_ptr) detect_formats = snd_dice_stream_detect_current_formats; else - detect_formats = (snd_dice_detect_formats_t)entry->driver_data; + detect_formats = entry->driver_data_ptr; // Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer // frequency. @@ -255,7 +255,7 @@ static void dice_bus_reset(struct fw_unit *unit) .model_id = (model), \ .specifier_id = (vendor), \ .version = DICE_INTERFACE, \ - .driver_data = (kernel_ulong_t)(data), \ + .driver_data_ptr = (data), \ } static const struct ieee1394_device_id dice_id_table[] = { @@ -267,7 +267,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MAUDIO, .model_id = 0x000010, - .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + .driver_data_ptr = snd_dice_detect_extension_formats, }, /* M-Audio Profire 610 has a different value in version field. */ { @@ -275,7 +275,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MAUDIO, .model_id = 0x000011, - .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + .driver_data_ptr = snd_dice_detect_extension_formats, }, /* TC Electronic Konnekt 24D. */ { @@ -283,7 +283,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000020, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Konnekt 8. */ { @@ -291,7 +291,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000021, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Studio Konnekt 48. */ { @@ -299,7 +299,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000022, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Konnekt Live. */ { @@ -307,7 +307,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000023, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Desktop Konnekt 6. */ { @@ -315,7 +315,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000024, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Impact Twin. */ { @@ -323,7 +323,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000027, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Digital Konnekt x32. */ { @@ -331,7 +331,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000030, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* Alesis iO14/iO26. */ { @@ -339,7 +339,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_ALESIS, .model_id = MODEL_ALESIS_IO_BOTH, - .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats, + .driver_data_ptr = snd_dice_detect_alesis_formats, }, // Alesis MasterControl. { @@ -347,7 +347,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_ALESIS, .model_id = 0x000002, - .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_mastercontrol_formats, + .driver_data_ptr = snd_dice_detect_alesis_mastercontrol_formats, }, /* Mytek Stereo 192 DSD-DAC. */ { @@ -355,7 +355,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MYTEK, .model_id = 0x000002, - .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats, + .driver_data_ptr = snd_dice_detect_mytek_formats, }, // Solid State Logic, Duende Classic and Mini. // NOTE: each field of GUID in config ROM is not compliant to standard @@ -469,7 +469,7 @@ static const struct ieee1394_device_id dice_id_table[] = { .model_id = OUI_TEAC, .specifier_id = OUI_TEAC, .version = 0x800006, - .driver_data = (kernel_ulong_t)snd_dice_detect_teac_formats, + .driver_data_ptr = snd_dice_detect_teac_formats, }, { } }; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 5d2c4fbf4434..13472822d2be 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -70,7 +70,7 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e init_waitqueue_head(&ff->hwdep_wait); ff->unit_version = entry->version; - ff->spec = (const struct snd_ff_spec *)entry->driver_data; + ff->spec = entry->driver_data_ptr; err = snd_ff_transaction_register(ff); if (err < 0) @@ -186,7 +186,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_FF800, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ff800, + .driver_data_ptr = &spec_ff800, }, /* Fireface 400 */ { @@ -198,7 +198,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_FF400, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ff400, + .driver_data_ptr = &spec_ff400, }, // Fireface UFX. { @@ -210,7 +210,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_UFX, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ufx_802, + .driver_data_ptr = &spec_ufx_802, }, // Fireface UCX. { @@ -222,7 +222,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_UCX, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ucx, + .driver_data_ptr = &spec_ucx, }, // Fireface 802. { @@ -234,7 +234,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_802, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ufx_802, + .driver_data_ptr = &spec_ufx_802, }, {} }; diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index fd2a9dddbfa6..1fec6c8cdf6c 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -78,7 +78,7 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent dev_set_drvdata(&unit->device, motu); motu->card = card; - motu->spec = (const struct snd_motu_spec *)entry->driver_data; + motu->spec = entry->driver_data_ptr; mutex_init(&motu->mutex); spin_lock_init(&motu->lock); init_waitqueue_head(&motu->hwdep_wait); @@ -148,7 +148,7 @@ static void motu_bus_update(struct fw_unit *unit) snd_motu_transaction_reregister(motu); } -#define SND_MOTU_DEV_ENTRY(model, data) \ +#define SND_MOTU_DEV_ENTRY(model, data_ptr) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ IEEE1394_MATCH_SPECIFIER_ID | \ @@ -156,7 +156,7 @@ static void motu_bus_update(struct fw_unit *unit) .vendor_id = OUI_MOTU, \ .specifier_id = OUI_MOTU, \ .version = model, \ - .driver_data = (kernel_ulong_t)data, \ + .driver_data_ptr = data_ptr, \ } static const struct ieee1394_device_id motu_id_table[] = { diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 5039bd79b18e..38a3c3b150df 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -95,7 +95,7 @@ static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *ent /* to apply card definitions */ if (entry->vendor_id == VENDOR_GRIFFIN || entry->vendor_id == VENDOR_LACIE) { - info = (const struct compat_info *)entry->driver_data; + info = entry->driver_data_ptr; d = info->driver_name; v = info->vendor_name; m = info->model_name; @@ -321,7 +321,7 @@ static const struct compat_info lacie_speakers = { .model_id = model, \ .specifier_id = SPECIFIER_1394TA, \ .version = VERSION_AVC, \ - .driver_data = (kernel_ulong_t)data, \ + .driver_data_ptr = data, \ } static const struct ieee1394_device_id oxfw_id_table[] = { From 7cc812e634bdea9958606eaa9d5ba3e5ec52e6b7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:38 +0900 Subject: [PATCH 14/17] firewire: core: minor code refactoring for case-dependent parameters of iso resources management The generation parameter is specific to the auto case of iso resources management, while it is in the common parameter structure. Move the generation member to the structure specific to auto case. Link: https://lore.kernel.org/r/20260520130840.629934-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c166e7617d2a..c669c9e42d34 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -129,7 +129,6 @@ struct descriptor_resource { }; struct iso_resource_params { - int generation; u64 channels; s32 bandwidth; }; @@ -144,6 +143,7 @@ struct iso_resource_auto { ISO_RES_AUTO_REALLOC, ISO_RES_AUTO_DEALLOC, } todo; + int generation; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1316,7 +1316,6 @@ static int fill_iso_resource_params(struct iso_resource_params *params, request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) return -EINVAL; - params->generation = -1; params->channels = request->channels; params->bandwidth = request->bandwidth; @@ -1336,8 +1335,8 @@ static void iso_resource_auto_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; current_generation = client->device->generation; - resource_generation = r->params.generation; - r->params.generation = current_generation; + resource_generation = r->generation; + r->generation = current_generation; todo = r->todo; } @@ -1495,7 +1494,6 @@ static void iso_resource_once_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) generation = client->device->generation; - r->params.generation = generation; bandwidth = r->params.bandwidth; fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, From 9e38ee1c5522b1b6ba62e0766202bcc9979d6ae3 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:39 +0900 Subject: [PATCH 15/17] firewire: core: rename member name for channel mask of isoc resource The iso_resource_params structure has a member for channel mask, while the name of field is easy to misinterpret. Append _mask to the member name. Link: https://lore.kernel.org/r/20260520130840.629934-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c669c9e42d34..56c21cabc20c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -129,7 +129,7 @@ struct descriptor_resource { }; struct iso_resource_params { - u64 channels; + u64 channels_mask; s32 bandwidth; }; @@ -1316,7 +1316,7 @@ static int fill_iso_resource_params(struct iso_resource_params *params, request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) return -EINVAL; - params->channels = request->channels; + params->channels_mask = request->channels; params->bandwidth = request->bandwidth; return 0; @@ -1360,7 +1360,7 @@ static void iso_resource_auto_work(struct work_struct *work) bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels_mask, &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); if (todo == ISO_RES_AUTO_DEALLOC) { @@ -1402,7 +1402,7 @@ static void iso_resource_auto_work(struct work_struct *work) r->todo = ISO_RES_AUTO_REALLOC; if (channel >= 0) - r->params.channels = 1ULL << channel; + r->params.channels_mask = BIT_ULL(channel); e = r->e_alloc; r->e_alloc = NULL; @@ -1496,7 +1496,7 @@ static void iso_resource_once_work(struct work_struct *work) bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, + fw_iso_resource_manage(client->device->card, generation, r->params.channels_mask, &channel, &bandwidth, r->todo == ISO_RES_ONCE_ALLOC); e->iso_resource.handle = UNAVAILABLE_HANDLE; From 21e163988c87219b3973efe9ca934b7acf0e4fe8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:40 +0900 Subject: [PATCH 16/17] firewire: core: cancel using delayed work for iso_resource_once management There is no need to use deferrable type of work for iso_resource_once management because the work is queued to run immediately. Link: https://lore.kernel.org/r/20260520130840.629934-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 56c21cabc20c..e49d8a58be09 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -150,8 +150,7 @@ struct iso_resource_auto { struct iso_resource_once { struct client *client; - // Schedule work and access todo only with client->lock held. - struct delayed_work work; + struct work_struct work; enum { ISO_RES_ONCE_ALLOC, ISO_RES_ONCE_DEALLOC, @@ -1486,7 +1485,7 @@ static int ioctl_deallocate_iso_resource(struct client *client, static void iso_resource_once_work(struct work_struct *work) { - struct iso_resource_once *r = from_work(r, work, work.work); + struct iso_resource_once *r = from_work(r, work, work); struct client *client = r->client; struct iso_resource_event *e = r->event; int generation, channel, bandwidth; @@ -1505,7 +1504,7 @@ static void iso_resource_once_work(struct work_struct *work) queue_event(client, &e->event, &e->iso_resource, sizeof(e->iso_resource), NULL, 0); - cancel_delayed_work(&r->work); + cancel_work(&r->work); kfree(r); client_put(client); @@ -1525,7 +1524,7 @@ static int init_iso_resource_once(struct client *client, if (err < 0) return err; - INIT_DELAYED_WORK(&r->work, iso_resource_once_work); + INIT_WORK(&r->work, iso_resource_once_work); r->client = client; r->todo = todo; @@ -1539,7 +1538,7 @@ static int init_iso_resource_once(struct client *client, // Keep the client until work item finishing. client_get(r->client); - queue_delayed_work(fw_workqueue, &no_free_ptr(r)->work, 0); + queue_work(fw_workqueue, &no_free_ptr(r)->work); request->handle = UNAVAILABLE_HANDLE; From e954726de56963754c8ac9d9e76b3f59d12fef17 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Tue, 9 Jun 2026 14:13:35 +0800 Subject: [PATCH 17/17] firewire: core: Open-code topology list walk A later change will make list_for_each_entry() cache the next element before entering the loop body. for_each_fw_node() intentionally appends newly discovered child nodes to the temporary walk list while the list is being traversed. Keep the loop open-coded so the next node is looked up only after children have been appended. This preserves the current breadth-first traversal semantics and prepares the code for the list iterator update. Signed-off-by: Kaitao Cheng Link: https://lore.kernel.org/r/20260609061347.93688-3-kaitao.cheng@linux.dev Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index bb2d2db30795..df2ac0dab106 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -272,7 +272,9 @@ static void for_each_fw_node(struct fw_card *card, struct fw_node *root, fw_node_get(root); list_add_tail(&root->link, &list); parent = NULL; - list_for_each_entry(node, &list, link) { + for (node = list_first_entry(&list, typeof(*node), link); + !list_entry_is_head(node, &list, link); + node = list_next_entry(node, link)) { node->color = card->color; for (i = 0; i < node->port_count; i++) {