Commit 1ec3c026 authored by Stefan Richter's avatar Stefan Richter

firewire: cdev: add ioctls for manual iso resource management

This adds ioctls for allocation and deallocation of a channel or/and
bandwidth without auto-reallocation and without auto-deallocation.

The benefit of these ioctls is that libraw1394-style isochronous
resource management can be implemented without write access to the IRM's
character device file.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent b1bda4cd
...@@ -121,14 +121,15 @@ struct iso_resource { ...@@ -121,14 +121,15 @@ struct iso_resource {
struct client *client; struct client *client;
/* Schedule work and access todo only with client->lock held. */ /* Schedule work and access todo only with client->lock held. */
struct delayed_work work; struct delayed_work work;
enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,} todo; enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
int generation; int generation;
u64 channels; u64 channels;
s32 bandwidth; s32 bandwidth;
struct iso_resource_event *e_alloc, *e_dealloc; struct iso_resource_event *e_alloc, *e_dealloc;
}; };
static void schedule_iso_resource(struct iso_resource *); static int schedule_iso_resource(struct iso_resource *);
static void release_iso_resource(struct client *, struct client_resource *); static void release_iso_resource(struct client *, struct client_resource *);
/* /*
...@@ -1033,7 +1034,9 @@ static void iso_resource_work(struct work_struct *work) ...@@ -1033,7 +1034,9 @@ static void iso_resource_work(struct work_struct *work)
skip = todo == ISO_RES_REALLOC && skip = todo == ISO_RES_REALLOC &&
r->generation == generation; r->generation == generation;
} }
free = todo == ISO_RES_DEALLOC; free = todo == ISO_RES_DEALLOC ||
todo == ISO_RES_ALLOC_ONCE ||
todo == ISO_RES_DEALLOC_ONCE;
r->generation = generation; r->generation = generation;
spin_unlock_irq(&client->lock); spin_unlock_irq(&client->lock);
...@@ -1044,7 +1047,9 @@ static void iso_resource_work(struct work_struct *work) ...@@ -1044,7 +1047,9 @@ static void iso_resource_work(struct work_struct *work)
fw_iso_resource_manage(client->device->card, generation, fw_iso_resource_manage(client->device->card, generation,
r->channels, &channel, &bandwidth, r->channels, &channel, &bandwidth,
todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC); todo == ISO_RES_ALLOC ||
todo == ISO_RES_REALLOC ||
todo == ISO_RES_ALLOC_ONCE);
/* /*
* Is this generation outdated already? As long as this resource sticks * Is this generation outdated already? As long as this resource sticks
* in the idr, it will be scheduled again for a newer generation or at * in the idr, it will be scheduled again for a newer generation or at
...@@ -1082,7 +1087,7 @@ static void iso_resource_work(struct work_struct *work) ...@@ -1082,7 +1087,7 @@ static void iso_resource_work(struct work_struct *work)
if (todo == ISO_RES_REALLOC && success) if (todo == ISO_RES_REALLOC && success)
goto out; goto out;
if (todo == ISO_RES_ALLOC) { if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
e = r->e_alloc; e = r->e_alloc;
r->e_alloc = NULL; r->e_alloc = NULL;
} else { } else {
...@@ -1106,10 +1111,17 @@ static void iso_resource_work(struct work_struct *work) ...@@ -1106,10 +1111,17 @@ static void iso_resource_work(struct work_struct *work)
client_put(client); client_put(client);
} }
static void schedule_iso_resource(struct iso_resource *r) static int schedule_iso_resource(struct iso_resource *r)
{ {
if (schedule_delayed_work(&r->work, 0)) int scheduled;
client_get(r->client); client_get(r->client);
scheduled = schedule_delayed_work(&r->work, 0);
if (!scheduled)
client_put(r->client);
return scheduled;
} }
static void release_iso_resource(struct client *client, static void release_iso_resource(struct client *client,
...@@ -1124,9 +1136,9 @@ static void release_iso_resource(struct client *client, ...@@ -1124,9 +1136,9 @@ static void release_iso_resource(struct client *client,
spin_unlock_irq(&client->lock); spin_unlock_irq(&client->lock);
} }
static int ioctl_allocate_iso_resource(struct client *client, void *buffer) static int init_iso_resource(struct client *client,
struct fw_cdev_allocate_iso_resource *request, int todo)
{ {
struct fw_cdev_allocate_iso_resource *request = buffer;
struct iso_resource_event *e1, *e2; struct iso_resource_event *e1, *e2;
struct iso_resource *r; struct iso_resource *r;
int ret; int ret;
...@@ -1146,7 +1158,7 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer) ...@@ -1146,7 +1158,7 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
INIT_DELAYED_WORK(&r->work, iso_resource_work); INIT_DELAYED_WORK(&r->work, iso_resource_work);
r->client = client; r->client = client;
r->todo = ISO_RES_ALLOC; r->todo = todo;
r->generation = -1; r->generation = -1;
r->channels = request->channels; r->channels = request->channels;
r->bandwidth = request->bandwidth; r->bandwidth = request->bandwidth;
...@@ -1158,8 +1170,14 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer) ...@@ -1158,8 +1170,14 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
e2->resource.closure = request->closure; e2->resource.closure = request->closure;
e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
if (todo == ISO_RES_ALLOC) {
r->resource.release = release_iso_resource; r->resource.release = release_iso_resource;
ret = add_client_resource(client, &r->resource, GFP_KERNEL); ret = add_client_resource(client, &r->resource, GFP_KERNEL);
} else {
r->resource.release = NULL;
r->resource.handle = -1;
ret = schedule_iso_resource(r) ? 0 : -ENOMEM;
}
if (ret < 0) if (ret < 0)
goto fail; goto fail;
request->handle = r->resource.handle; request->handle = r->resource.handle;
...@@ -1173,6 +1191,13 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer) ...@@ -1173,6 +1191,13 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
return ret; return ret;
} }
static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
{
struct fw_cdev_allocate_iso_resource *request = buffer;
return init_iso_resource(client, request, ISO_RES_ALLOC);
}
static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
{ {
struct fw_cdev_deallocate *request = buffer; struct fw_cdev_deallocate *request = buffer;
...@@ -1181,6 +1206,20 @@ static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) ...@@ -1181,6 +1206,20 @@ static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
release_iso_resource, NULL); release_iso_resource, NULL);
} }
static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
{
struct fw_cdev_allocate_iso_resource *request = buffer;
return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
}
static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
{
struct fw_cdev_allocate_iso_resource *request = buffer;
return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
}
static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_get_info, ioctl_get_info,
ioctl_send_request, ioctl_send_request,
...@@ -1197,6 +1236,8 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { ...@@ -1197,6 +1236,8 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_get_cycle_timer, ioctl_get_cycle_timer,
ioctl_allocate_iso_resource, ioctl_allocate_iso_resource,
ioctl_deallocate_iso_resource, ioctl_deallocate_iso_resource,
ioctl_allocate_iso_resource_once,
ioctl_deallocate_iso_resource_once,
}; };
static int dispatch_ioctl(struct client *client, static int dispatch_ioctl(struct client *client,
......
...@@ -151,7 +151,7 @@ struct fw_cdev_event_iso_interrupt { ...@@ -151,7 +151,7 @@ struct fw_cdev_event_iso_interrupt {
/** /**
* struct fw_cdev_event_iso_resource - Iso resources were allocated or freed * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed
* @closure: See &fw_cdev_event_common; * @closure: See &fw_cdev_event_common;
* set by %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl
* @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
* %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
* @handle: Reference by which an allocated resource can be deallocated * @handle: Reference by which an allocated resource can be deallocated
...@@ -164,12 +164,12 @@ struct fw_cdev_event_iso_interrupt { ...@@ -164,12 +164,12 @@ struct fw_cdev_event_iso_interrupt {
* resource was allocated at the IRM. The client has to check @channel and * resource was allocated at the IRM. The client has to check @channel and
* @bandwidth for whether the allocation actually succeeded. * @bandwidth for whether the allocation actually succeeded.
* *
* @channel is <0 if no channel was allocated.
* @bandwidth is 0 if no bandwidth was allocated.
*
* An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous
* resource was deallocated at the IRM. It is also sent when automatic * resource was deallocated at the IRM. It is also sent when automatic
* reallocation after a bus reset failed. * reallocation after a bus reset failed.
*
* @channel is <0 if no channel was (de)allocated or if reallocation failed.
* @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed.
*/ */
struct fw_cdev_event_iso_resource { struct fw_cdev_event_iso_resource {
__u64 closure; __u64 closure;
...@@ -227,6 +227,8 @@ union fw_cdev_event { ...@@ -227,6 +227,8 @@ union fw_cdev_event {
/* available since kernel version 2.6.30 */ /* available since kernel version 2.6.30 */
#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) #define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource)
#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate) #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate)
#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource)
#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource)
/* FW_CDEV_VERSION History /* FW_CDEV_VERSION History
* *
...@@ -523,11 +525,12 @@ struct fw_cdev_get_cycle_timer { ...@@ -523,11 +525,12 @@ struct fw_cdev_get_cycle_timer {
}; };
/** /**
* struct fw_cdev_allocate_iso_resource - Allocate a channel or bandwidth * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth
* @closure: Passed back to userspace in correponding iso resource events * @closure: Passed back to userspace in correponding iso resource events
* @channels: Isochronous channels of which one is to be allocated * @channels: Isochronous channels of which one is to be (de)allocated
* @bandwidth: Isochronous bandwidth units to be allocated * @bandwidth: Isochronous bandwidth units to be (de)allocated
* @handle: Handle to the allocation, written by the kernel * @handle: Handle to the allocation, written by the kernel (only valid in
* case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls)
* *
* The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an
* isochronous channel and/or of isochronous bandwidth at the isochronous * isochronous channel and/or of isochronous bandwidth at the isochronous
...@@ -539,6 +542,25 @@ struct fw_cdev_get_cycle_timer { ...@@ -539,6 +542,25 @@ struct fw_cdev_get_cycle_timer {
* will be sent. The kernel will also automatically deallocate the resources * will be sent. The kernel will also automatically deallocate the resources
* when the file descriptor is closed. * when the file descriptor is closed.
* *
* The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate
* deallocation of resources which were allocated as described above.
* An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
*
* The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation
* without automatic re- or deallocation.
* An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation,
* indicating success or failure in its data.
*
* The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like
* %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed
* instead of allocated. At most one channel may be specified in this ioctl.
* An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
*
* To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources
* for the lifetime of the fd or handle.
* In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources
* for the duration of a bus generation.
*
* @channels is a host-endian bitfield with the most significant bit * @channels is a host-endian bitfield with the most significant bit
* representing channel 0 and the least significant bit representing channel 63: * representing channel 0 and the least significant bit representing channel 63:
* 1ULL << (63 - c) * 1ULL << (63 - c)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment