Commit 1ec3c0269d7196118cc7c403654ca5f19ef4d584
1 parent
b1bda4cdc2
Exists in
master
and in
7 other branches
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: Stefan Richter <stefanr@s5r6.in-berlin.de>
Showing 2 changed files with 86 additions and 23 deletions Side-by-side Diff
drivers/firewire/fw-cdev.c
... | ... | @@ -121,14 +121,15 @@ |
121 | 121 | struct client *client; |
122 | 122 | /* Schedule work and access todo only with client->lock held. */ |
123 | 123 | struct delayed_work work; |
124 | - enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,} todo; | |
124 | + enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, | |
125 | + ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; | |
125 | 126 | int generation; |
126 | 127 | u64 channels; |
127 | 128 | s32 bandwidth; |
128 | 129 | struct iso_resource_event *e_alloc, *e_dealloc; |
129 | 130 | }; |
130 | 131 | |
131 | -static void schedule_iso_resource(struct iso_resource *); | |
132 | +static int schedule_iso_resource(struct iso_resource *); | |
132 | 133 | static void release_iso_resource(struct client *, struct client_resource *); |
133 | 134 | |
134 | 135 | /* |
... | ... | @@ -1033,7 +1034,9 @@ |
1033 | 1034 | skip = todo == ISO_RES_REALLOC && |
1034 | 1035 | r->generation == generation; |
1035 | 1036 | } |
1036 | - free = todo == ISO_RES_DEALLOC; | |
1037 | + free = todo == ISO_RES_DEALLOC || | |
1038 | + todo == ISO_RES_ALLOC_ONCE || | |
1039 | + todo == ISO_RES_DEALLOC_ONCE; | |
1037 | 1040 | r->generation = generation; |
1038 | 1041 | spin_unlock_irq(&client->lock); |
1039 | 1042 | |
... | ... | @@ -1044,7 +1047,9 @@ |
1044 | 1047 | |
1045 | 1048 | fw_iso_resource_manage(client->device->card, generation, |
1046 | 1049 | r->channels, &channel, &bandwidth, |
1047 | - todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC); | |
1050 | + todo == ISO_RES_ALLOC || | |
1051 | + todo == ISO_RES_REALLOC || | |
1052 | + todo == ISO_RES_ALLOC_ONCE); | |
1048 | 1053 | /* |
1049 | 1054 | * Is this generation outdated already? As long as this resource sticks |
1050 | 1055 | * in the idr, it will be scheduled again for a newer generation or at |
... | ... | @@ -1082,7 +1087,7 @@ |
1082 | 1087 | if (todo == ISO_RES_REALLOC && success) |
1083 | 1088 | goto out; |
1084 | 1089 | |
1085 | - if (todo == ISO_RES_ALLOC) { | |
1090 | + if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { | |
1086 | 1091 | e = r->e_alloc; |
1087 | 1092 | r->e_alloc = NULL; |
1088 | 1093 | } else { |
1089 | 1094 | |
... | ... | @@ -1106,10 +1111,17 @@ |
1106 | 1111 | client_put(client); |
1107 | 1112 | } |
1108 | 1113 | |
1109 | -static void schedule_iso_resource(struct iso_resource *r) | |
1114 | +static int schedule_iso_resource(struct iso_resource *r) | |
1110 | 1115 | { |
1111 | - if (schedule_delayed_work(&r->work, 0)) | |
1112 | - client_get(r->client); | |
1116 | + int scheduled; | |
1117 | + | |
1118 | + client_get(r->client); | |
1119 | + | |
1120 | + scheduled = schedule_delayed_work(&r->work, 0); | |
1121 | + if (!scheduled) | |
1122 | + client_put(r->client); | |
1123 | + | |
1124 | + return scheduled; | |
1113 | 1125 | } |
1114 | 1126 | |
1115 | 1127 | static void release_iso_resource(struct client *client, |
1116 | 1128 | |
... | ... | @@ -1124,9 +1136,9 @@ |
1124 | 1136 | spin_unlock_irq(&client->lock); |
1125 | 1137 | } |
1126 | 1138 | |
1127 | -static int ioctl_allocate_iso_resource(struct client *client, void *buffer) | |
1139 | +static int init_iso_resource(struct client *client, | |
1140 | + struct fw_cdev_allocate_iso_resource *request, int todo) | |
1128 | 1141 | { |
1129 | - struct fw_cdev_allocate_iso_resource *request = buffer; | |
1130 | 1142 | struct iso_resource_event *e1, *e2; |
1131 | 1143 | struct iso_resource *r; |
1132 | 1144 | int ret; |
... | ... | @@ -1146,7 +1158,7 @@ |
1146 | 1158 | |
1147 | 1159 | INIT_DELAYED_WORK(&r->work, iso_resource_work); |
1148 | 1160 | r->client = client; |
1149 | - r->todo = ISO_RES_ALLOC; | |
1161 | + r->todo = todo; | |
1150 | 1162 | r->generation = -1; |
1151 | 1163 | r->channels = request->channels; |
1152 | 1164 | r->bandwidth = request->bandwidth; |
... | ... | @@ -1158,8 +1170,14 @@ |
1158 | 1170 | e2->resource.closure = request->closure; |
1159 | 1171 | e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; |
1160 | 1172 | |
1161 | - r->resource.release = release_iso_resource; | |
1162 | - ret = add_client_resource(client, &r->resource, GFP_KERNEL); | |
1173 | + if (todo == ISO_RES_ALLOC) { | |
1174 | + r->resource.release = release_iso_resource; | |
1175 | + ret = add_client_resource(client, &r->resource, GFP_KERNEL); | |
1176 | + } else { | |
1177 | + r->resource.release = NULL; | |
1178 | + r->resource.handle = -1; | |
1179 | + ret = schedule_iso_resource(r) ? 0 : -ENOMEM; | |
1180 | + } | |
1163 | 1181 | if (ret < 0) |
1164 | 1182 | goto fail; |
1165 | 1183 | request->handle = r->resource.handle; |
... | ... | @@ -1173,6 +1191,13 @@ |
1173 | 1191 | return ret; |
1174 | 1192 | } |
1175 | 1193 | |
1194 | +static int ioctl_allocate_iso_resource(struct client *client, void *buffer) | |
1195 | +{ | |
1196 | + struct fw_cdev_allocate_iso_resource *request = buffer; | |
1197 | + | |
1198 | + return init_iso_resource(client, request, ISO_RES_ALLOC); | |
1199 | +} | |
1200 | + | |
1176 | 1201 | static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) |
1177 | 1202 | { |
1178 | 1203 | struct fw_cdev_deallocate *request = buffer; |
... | ... | @@ -1181,6 +1206,20 @@ |
1181 | 1206 | release_iso_resource, NULL); |
1182 | 1207 | } |
1183 | 1208 | |
1209 | +static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer) | |
1210 | +{ | |
1211 | + struct fw_cdev_allocate_iso_resource *request = buffer; | |
1212 | + | |
1213 | + return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE); | |
1214 | +} | |
1215 | + | |
1216 | +static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer) | |
1217 | +{ | |
1218 | + struct fw_cdev_allocate_iso_resource *request = buffer; | |
1219 | + | |
1220 | + return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE); | |
1221 | +} | |
1222 | + | |
1184 | 1223 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { |
1185 | 1224 | ioctl_get_info, |
1186 | 1225 | ioctl_send_request, |
... | ... | @@ -1197,6 +1236,8 @@ |
1197 | 1236 | ioctl_get_cycle_timer, |
1198 | 1237 | ioctl_allocate_iso_resource, |
1199 | 1238 | ioctl_deallocate_iso_resource, |
1239 | + ioctl_allocate_iso_resource_once, | |
1240 | + ioctl_deallocate_iso_resource_once, | |
1200 | 1241 | }; |
1201 | 1242 | |
1202 | 1243 | static int dispatch_ioctl(struct client *client, |
include/linux/firewire-cdev.h
... | ... | @@ -151,7 +151,7 @@ |
151 | 151 | /** |
152 | 152 | * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed |
153 | 153 | * @closure: See &fw_cdev_event_common; |
154 | - * set by %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl | |
154 | + * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl | |
155 | 155 | * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or |
156 | 156 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED |
157 | 157 | * @handle: Reference by which an allocated resource can be deallocated |
158 | 158 | |
... | ... | @@ -164,12 +164,12 @@ |
164 | 164 | * resource was allocated at the IRM. The client has to check @channel and |
165 | 165 | * @bandwidth for whether the allocation actually succeeded. |
166 | 166 | * |
167 | - * @channel is <0 if no channel was allocated. | |
168 | - * @bandwidth is 0 if no bandwidth was allocated. | |
169 | - * | |
170 | 167 | * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous |
171 | 168 | * resource was deallocated at the IRM. It is also sent when automatic |
172 | 169 | * reallocation after a bus reset failed. |
170 | + * | |
171 | + * @channel is <0 if no channel was (de)allocated or if reallocation failed. | |
172 | + * @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed. | |
173 | 173 | */ |
174 | 174 | struct fw_cdev_event_iso_resource { |
175 | 175 | __u64 closure; |
... | ... | @@ -225,8 +225,10 @@ |
225 | 225 | #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer) |
226 | 226 | |
227 | 227 | /* available since kernel version 2.6.30 */ |
228 | -#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) | |
229 | -#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate) | |
228 | +#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) | |
229 | +#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate) | |
230 | +#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource) | |
231 | +#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource) | |
230 | 232 | |
231 | 233 | /* FW_CDEV_VERSION History |
232 | 234 | * |
233 | 235 | |
... | ... | @@ -523,11 +525,12 @@ |
523 | 525 | }; |
524 | 526 | |
525 | 527 | /** |
526 | - * struct fw_cdev_allocate_iso_resource - Allocate a channel or bandwidth | |
528 | + * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth | |
527 | 529 | * @closure: Passed back to userspace in correponding iso resource events |
528 | - * @channels: Isochronous channels of which one is to be allocated | |
529 | - * @bandwidth: Isochronous bandwidth units to be allocated | |
530 | - * @handle: Handle to the allocation, written by the kernel | |
530 | + * @channels: Isochronous channels of which one is to be (de)allocated | |
531 | + * @bandwidth: Isochronous bandwidth units to be (de)allocated | |
532 | + * @handle: Handle to the allocation, written by the kernel (only valid in | |
533 | + * case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls) | |
531 | 534 | * |
532 | 535 | * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an |
533 | 536 | * isochronous channel and/or of isochronous bandwidth at the isochronous |
... | ... | @@ -538,6 +541,25 @@ |
538 | 541 | * Should a reallocation fail, an %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event |
539 | 542 | * will be sent. The kernel will also automatically deallocate the resources |
540 | 543 | * when the file descriptor is closed. |
544 | + * | |
545 | + * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate | |
546 | + * deallocation of resources which were allocated as described above. | |
547 | + * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation. | |
548 | + * | |
549 | + * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation | |
550 | + * without automatic re- or deallocation. | |
551 | + * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation, | |
552 | + * indicating success or failure in its data. | |
553 | + * | |
554 | + * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like | |
555 | + * %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed | |
556 | + * instead of allocated. At most one channel may be specified in this ioctl. | |
557 | + * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation. | |
558 | + * | |
559 | + * To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources | |
560 | + * for the lifetime of the fd or handle. | |
561 | + * In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources | |
562 | + * for the duration of a bus generation. | |
541 | 563 | * |
542 | 564 | * @channels is a host-endian bitfield with the most significant bit |
543 | 565 | * representing channel 0 and the least significant bit representing channel 63: |