diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r-- | drivers/firewire/core-cdev.c | 113 |
1 files changed, 67 insertions, 46 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 5089331544ed..231e6ee5ba43 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -130,9 +130,22 @@ struct iso_resource { | |||
130 | struct iso_resource_event *e_alloc, *e_dealloc; | 130 | struct iso_resource_event *e_alloc, *e_dealloc; |
131 | }; | 131 | }; |
132 | 132 | ||
133 | static void schedule_iso_resource(struct iso_resource *); | ||
134 | static void release_iso_resource(struct client *, struct client_resource *); | 133 | static void release_iso_resource(struct client *, struct client_resource *); |
135 | 134 | ||
135 | static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) | ||
136 | { | ||
137 | client_get(r->client); | ||
138 | if (!schedule_delayed_work(&r->work, delay)) | ||
139 | client_put(r->client); | ||
140 | } | ||
141 | |||
142 | static void schedule_if_iso_resource(struct client_resource *resource) | ||
143 | { | ||
144 | if (resource->release == release_iso_resource) | ||
145 | schedule_iso_resource(container_of(resource, | ||
146 | struct iso_resource, resource), 0); | ||
147 | } | ||
148 | |||
136 | /* | 149 | /* |
137 | * dequeue_event() just kfree()'s the event, so the event has to be | 150 | * dequeue_event() just kfree()'s the event, so the event has to be |
138 | * the first field in a struct XYZ_event. | 151 | * the first field in a struct XYZ_event. |
@@ -166,7 +179,7 @@ struct iso_interrupt_event { | |||
166 | 179 | ||
167 | struct iso_resource_event { | 180 | struct iso_resource_event { |
168 | struct event event; | 181 | struct event event; |
169 | struct fw_cdev_event_iso_resource resource; | 182 | struct fw_cdev_event_iso_resource iso_resource; |
170 | }; | 183 | }; |
171 | 184 | ||
172 | static inline void __user *u64_to_uptr(__u64 value) | 185 | static inline void __user *u64_to_uptr(__u64 value) |
@@ -314,11 +327,8 @@ static void for_each_client(struct fw_device *device, | |||
314 | 327 | ||
315 | static int schedule_reallocations(int id, void *p, void *data) | 328 | static int schedule_reallocations(int id, void *p, void *data) |
316 | { | 329 | { |
317 | struct client_resource *r = p; | 330 | schedule_if_iso_resource(p); |
318 | 331 | ||
319 | if (r->release == release_iso_resource) | ||
320 | schedule_iso_resource(container_of(r, | ||
321 | struct iso_resource, resource)); | ||
322 | return 0; | 332 | return 0; |
323 | } | 333 | } |
324 | 334 | ||
@@ -414,9 +424,7 @@ static int add_client_resource(struct client *client, | |||
414 | &resource->handle); | 424 | &resource->handle); |
415 | if (ret >= 0) { | 425 | if (ret >= 0) { |
416 | client_get(client); | 426 | client_get(client); |
417 | if (resource->release == release_iso_resource) | 427 | schedule_if_iso_resource(resource); |
418 | schedule_iso_resource(container_of(resource, | ||
419 | struct iso_resource, resource)); | ||
420 | } | 428 | } |
421 | spin_unlock_irqrestore(&client->lock, flags); | 429 | spin_unlock_irqrestore(&client->lock, flags); |
422 | 430 | ||
@@ -428,26 +436,26 @@ static int add_client_resource(struct client *client, | |||
428 | 436 | ||
429 | static int release_client_resource(struct client *client, u32 handle, | 437 | static int release_client_resource(struct client *client, u32 handle, |
430 | client_resource_release_fn_t release, | 438 | client_resource_release_fn_t release, |
431 | struct client_resource **resource) | 439 | struct client_resource **return_resource) |
432 | { | 440 | { |
433 | struct client_resource *r; | 441 | struct client_resource *resource; |
434 | 442 | ||
435 | spin_lock_irq(&client->lock); | 443 | spin_lock_irq(&client->lock); |
436 | if (client->in_shutdown) | 444 | if (client->in_shutdown) |
437 | r = NULL; | 445 | resource = NULL; |
438 | else | 446 | else |
439 | r = idr_find(&client->resource_idr, handle); | 447 | resource = idr_find(&client->resource_idr, handle); |
440 | if (r && r->release == release) | 448 | if (resource && resource->release == release) |
441 | idr_remove(&client->resource_idr, handle); | 449 | idr_remove(&client->resource_idr, handle); |
442 | spin_unlock_irq(&client->lock); | 450 | spin_unlock_irq(&client->lock); |
443 | 451 | ||
444 | if (!(r && r->release == release)) | 452 | if (!(resource && resource->release == release)) |
445 | return -EINVAL; | 453 | return -EINVAL; |
446 | 454 | ||
447 | if (resource) | 455 | if (return_resource) |
448 | *resource = r; | 456 | *return_resource = resource; |
449 | else | 457 | else |
450 | r->release(client, r); | 458 | resource->release(client, resource); |
451 | 459 | ||
452 | client_put(client); | 460 | client_put(client); |
453 | 461 | ||
@@ -699,6 +707,7 @@ static int ioctl_send_response(struct client *client, void *buffer) | |||
699 | struct fw_cdev_send_response *request = buffer; | 707 | struct fw_cdev_send_response *request = buffer; |
700 | struct client_resource *resource; | 708 | struct client_resource *resource; |
701 | struct inbound_transaction_resource *r; | 709 | struct inbound_transaction_resource *r; |
710 | int ret = 0; | ||
702 | 711 | ||
703 | if (release_client_resource(client, request->handle, | 712 | if (release_client_resource(client, request->handle, |
704 | release_request, &resource) < 0) | 713 | release_request, &resource) < 0) |
@@ -708,13 +717,17 @@ static int ioctl_send_response(struct client *client, void *buffer) | |||
708 | resource); | 717 | resource); |
709 | if (request->length < r->length) | 718 | if (request->length < r->length) |
710 | r->length = request->length; | 719 | r->length = request->length; |
711 | if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) | 720 | |
712 | return -EFAULT; | 721 | if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) { |
722 | ret = -EFAULT; | ||
723 | goto out; | ||
724 | } | ||
713 | 725 | ||
714 | fw_send_response(client->device->card, r->request, request->rcode); | 726 | fw_send_response(client->device->card, r->request, request->rcode); |
727 | out: | ||
715 | kfree(r); | 728 | kfree(r); |
716 | 729 | ||
717 | return 0; | 730 | return ret; |
718 | } | 731 | } |
719 | 732 | ||
720 | static int ioctl_initiate_bus_reset(struct client *client, void *buffer) | 733 | static int ioctl_initiate_bus_reset(struct client *client, void *buffer) |
@@ -1028,8 +1041,7 @@ static void iso_resource_work(struct work_struct *work) | |||
1028 | /* Allow 1000ms grace period for other reallocations. */ | 1041 | /* Allow 1000ms grace period for other reallocations. */ |
1029 | if (todo == ISO_RES_ALLOC && | 1042 | if (todo == ISO_RES_ALLOC && |
1030 | time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { | 1043 | time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { |
1031 | if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3))) | 1044 | schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); |
1032 | client_get(client); | ||
1033 | skip = true; | 1045 | skip = true; |
1034 | } else { | 1046 | } else { |
1035 | /* We could be called twice within the same generation. */ | 1047 | /* We could be called twice within the same generation. */ |
@@ -1097,12 +1109,12 @@ static void iso_resource_work(struct work_struct *work) | |||
1097 | e = r->e_dealloc; | 1109 | e = r->e_dealloc; |
1098 | r->e_dealloc = NULL; | 1110 | r->e_dealloc = NULL; |
1099 | } | 1111 | } |
1100 | e->resource.handle = r->resource.handle; | 1112 | e->iso_resource.handle = r->resource.handle; |
1101 | e->resource.channel = channel; | 1113 | e->iso_resource.channel = channel; |
1102 | e->resource.bandwidth = bandwidth; | 1114 | e->iso_resource.bandwidth = bandwidth; |
1103 | 1115 | ||
1104 | queue_event(client, &e->event, | 1116 | queue_event(client, &e->event, |
1105 | &e->resource, sizeof(e->resource), NULL, 0); | 1117 | &e->iso_resource, sizeof(e->iso_resource), NULL, 0); |
1106 | 1118 | ||
1107 | if (free) { | 1119 | if (free) { |
1108 | cancel_delayed_work(&r->work); | 1120 | cancel_delayed_work(&r->work); |
@@ -1114,13 +1126,6 @@ static void iso_resource_work(struct work_struct *work) | |||
1114 | client_put(client); | 1126 | client_put(client); |
1115 | } | 1127 | } |
1116 | 1128 | ||
1117 | static void schedule_iso_resource(struct iso_resource *r) | ||
1118 | { | ||
1119 | client_get(r->client); | ||
1120 | if (!schedule_delayed_work(&r->work, 0)) | ||
1121 | client_put(r->client); | ||
1122 | } | ||
1123 | |||
1124 | static void release_iso_resource(struct client *client, | 1129 | static void release_iso_resource(struct client *client, |
1125 | struct client_resource *resource) | 1130 | struct client_resource *resource) |
1126 | { | 1131 | { |
@@ -1129,7 +1134,7 @@ static void release_iso_resource(struct client *client, | |||
1129 | 1134 | ||
1130 | spin_lock_irq(&client->lock); | 1135 | spin_lock_irq(&client->lock); |
1131 | r->todo = ISO_RES_DEALLOC; | 1136 | r->todo = ISO_RES_DEALLOC; |
1132 | schedule_iso_resource(r); | 1137 | schedule_iso_resource(r, 0); |
1133 | spin_unlock_irq(&client->lock); | 1138 | spin_unlock_irq(&client->lock); |
1134 | } | 1139 | } |
1135 | 1140 | ||
@@ -1162,10 +1167,10 @@ static int init_iso_resource(struct client *client, | |||
1162 | r->e_alloc = e1; | 1167 | r->e_alloc = e1; |
1163 | r->e_dealloc = e2; | 1168 | r->e_dealloc = e2; |
1164 | 1169 | ||
1165 | e1->resource.closure = request->closure; | 1170 | e1->iso_resource.closure = request->closure; |
1166 | e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; | 1171 | e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; |
1167 | e2->resource.closure = request->closure; | 1172 | e2->iso_resource.closure = request->closure; |
1168 | e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; | 1173 | e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; |
1169 | 1174 | ||
1170 | if (todo == ISO_RES_ALLOC) { | 1175 | if (todo == ISO_RES_ALLOC) { |
1171 | r->resource.release = release_iso_resource; | 1176 | r->resource.release = release_iso_resource; |
@@ -1175,7 +1180,7 @@ static int init_iso_resource(struct client *client, | |||
1175 | } else { | 1180 | } else { |
1176 | r->resource.release = NULL; | 1181 | r->resource.release = NULL; |
1177 | r->resource.handle = -1; | 1182 | r->resource.handle = -1; |
1178 | schedule_iso_resource(r); | 1183 | schedule_iso_resource(r, 0); |
1179 | } | 1184 | } |
1180 | request->handle = r->resource.handle; | 1185 | request->handle = r->resource.handle; |
1181 | 1186 | ||
@@ -1295,7 +1300,23 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | |||
1295 | static int dispatch_ioctl(struct client *client, | 1300 | static int dispatch_ioctl(struct client *client, |
1296 | unsigned int cmd, void __user *arg) | 1301 | unsigned int cmd, void __user *arg) |
1297 | { | 1302 | { |
1298 | char buffer[256]; | 1303 | char buffer[sizeof(union { |
1304 | struct fw_cdev_get_info _00; | ||
1305 | struct fw_cdev_send_request _01; | ||
1306 | struct fw_cdev_allocate _02; | ||
1307 | struct fw_cdev_deallocate _03; | ||
1308 | struct fw_cdev_send_response _04; | ||
1309 | struct fw_cdev_initiate_bus_reset _05; | ||
1310 | struct fw_cdev_add_descriptor _06; | ||
1311 | struct fw_cdev_remove_descriptor _07; | ||
1312 | struct fw_cdev_create_iso_context _08; | ||
1313 | struct fw_cdev_queue_iso _09; | ||
1314 | struct fw_cdev_start_iso _0a; | ||
1315 | struct fw_cdev_stop_iso _0b; | ||
1316 | struct fw_cdev_get_cycle_timer _0c; | ||
1317 | struct fw_cdev_allocate_iso_resource _0d; | ||
1318 | struct fw_cdev_send_stream_packet _13; | ||
1319 | })]; | ||
1299 | int ret; | 1320 | int ret; |
1300 | 1321 | ||
1301 | if (_IOC_TYPE(cmd) != '#' || | 1322 | if (_IOC_TYPE(cmd) != '#' || |
@@ -1390,10 +1411,10 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) | |||
1390 | 1411 | ||
1391 | static int shutdown_resource(int id, void *p, void *data) | 1412 | static int shutdown_resource(int id, void *p, void *data) |
1392 | { | 1413 | { |
1393 | struct client_resource *r = p; | 1414 | struct client_resource *resource = p; |
1394 | struct client *client = data; | 1415 | struct client *client = data; |
1395 | 1416 | ||
1396 | r->release(client, r); | 1417 | resource->release(client, resource); |
1397 | client_put(client); | 1418 | client_put(client); |
1398 | 1419 | ||
1399 | return 0; | 1420 | return 0; |
@@ -1402,7 +1423,7 @@ static int shutdown_resource(int id, void *p, void *data) | |||
1402 | static int fw_device_op_release(struct inode *inode, struct file *file) | 1423 | static int fw_device_op_release(struct inode *inode, struct file *file) |
1403 | { | 1424 | { |
1404 | struct client *client = file->private_data; | 1425 | struct client *client = file->private_data; |
1405 | struct event *e, *next_e; | 1426 | struct event *event, *next_event; |
1406 | 1427 | ||
1407 | mutex_lock(&client->device->client_list_mutex); | 1428 | mutex_lock(&client->device->client_list_mutex); |
1408 | list_del(&client->link); | 1429 | list_del(&client->link); |
@@ -1423,8 +1444,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
1423 | idr_remove_all(&client->resource_idr); | 1444 | idr_remove_all(&client->resource_idr); |
1424 | idr_destroy(&client->resource_idr); | 1445 | idr_destroy(&client->resource_idr); |
1425 | 1446 | ||
1426 | list_for_each_entry_safe(e, next_e, &client->event_list, link) | 1447 | list_for_each_entry_safe(event, next_event, &client->event_list, link) |
1427 | kfree(e); | 1448 | kfree(event); |
1428 | 1449 | ||
1429 | client_put(client); | 1450 | client_put(client); |
1430 | 1451 | ||