Commit 1ec3c0269d7196118cc7c403654ca5f19ef4d584

Authored by Stefan Richter
1 parent b1bda4cdc2

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: