diff options
| author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2026-04-29 18:34:45 +0900 |
|---|---|---|
| committer | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2026-04-29 20:30:33 +0900 |
| commit | b3ac3b453b23423e2c713d9ac497ddb0aec9aa7c (patch) | |
| tree | 1ed3470ae21e4684d1676a1f61528b42a556dc4c /drivers | |
| parent | e698cec3117fb26fc2550cd0858174484dd90cc2 (diff) | |
| download | linux-next-b3ac3b453b23423e2c713d9ac497ddb0aec9aa7c.tar.gz linux-next-b3ac3b453b23423e2c713d9ac497ddb0aec9aa7c.zip | |
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 <o-takashi@sakamocchi.jp>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/firewire/core-cdev.c | 83 |
1 files 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); } /* |
