* idr: IDs guaranteed to be less than 1<<31? (was Re: [PATCH] firewire: us an idr rather than a linked list for resources) [not found] <20081124163728.GA5826@redhat.com> @ 2008-11-24 19:09 ` Stefan Richter [not found] ` <492AEED8.9030102@s5r6.in-berlin.de> 1 sibling, 0 replies; 9+ messages in thread From: Stefan Richter @ 2008-11-24 19:09 UTC (permalink / raw) To: Jay Fenlason; +Cc: linux1394-devel, linux-kernel Jay Fenlason wrote at linux1394-devel: > As mentioned in the comments, there is a theoretical problem with this > code if someone manages to allocate 2^31 resources on a 32-bit > machine, or 2^32+1 resources on a 64+-bit machine. The kerneldoc of idr_get_new() says that we only get IDs in the range of 0...0x7fffffff. But is this true with 64bit kernels? -- Stefan Richter -=====-==--- =-== ==--- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <492AEED8.9030102@s5r6.in-berlin.de>]
[parent not found: <492AEF9D.5010307@s5r6.in-berlin.de>]
* [PATCH 0/4] firewire: cdev resources and events... [not found] ` <492AEF9D.5010307@s5r6.in-berlin.de> @ 2008-12-14 18:17 ` Stefan Richter 2008-12-14 18:19 ` [PATCH 1/4] firewire: cdev: fix race of fw_device_op_release with bus reset Stefan Richter ` (3 more replies) 0 siblings, 4 replies; 9+ messages in thread From: Stefan Richter @ 2008-12-14 18:17 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason Coming in follow-ups: 1/4 firewire: cdev: fix race of fw_device_op_release with bus reset 2/4 firewire: cdev: use an idr rather than a linked list for resources 3/4 firewire: cdev: address handler input validation 4/4 firewire: core: remove outdated comment drivers/firewire/fw-cdev.c | 180 ++++++++++++++++++++---------- drivers/firewire/fw-transaction.c | 34 +++-- 2 files changed, 142 insertions(+), 72 deletions(-) -- Stefan Richter -=====-==--- ==-- -===- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/4] firewire: cdev: fix race of fw_device_op_release with bus reset 2008-12-14 18:17 ` [PATCH 0/4] firewire: cdev resources and events Stefan Richter @ 2008-12-14 18:19 ` Stefan Richter 2008-12-14 18:20 ` [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources Stefan Richter ` (2 subsequent siblings) 3 siblings, 0 replies; 9+ messages in thread From: Stefan Richter @ 2008-12-14 18:19 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason Unlink the client from the fw_device earlier in order to prevent bus reset events being added to client->event_list during shutdown. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/firewire/fw-cdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) Index: linux/drivers/firewire/fw-cdev.c =================================================================== --- linux.orig/drivers/firewire/fw-cdev.c +++ linux/drivers/firewire/fw-cdev.c @@ -1009,6 +1009,10 @@ static int fw_device_op_release(struct i struct event *e, *next_e; struct client_resource *r, *next_r; + mutex_lock(&client->device->client_list_mutex); + list_del(&client->link); + mutex_unlock(&client->device->client_list_mutex); + if (client->buffer.pages) fw_iso_buffer_destroy(&client->buffer, client->device->card); @@ -1026,10 +1030,6 @@ static int fw_device_op_release(struct i list_for_each_entry_safe(e, next_e, &client->event_list, link) kfree(e); - mutex_lock(&client->device->client_list_mutex); - list_del(&client->link); - mutex_unlock(&client->device->client_list_mutex); - fw_device_put(client->device); kfree(client); -- Stefan Richter -=====-==--- ==-- -===- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources 2008-12-14 18:17 ` [PATCH 0/4] firewire: cdev resources and events Stefan Richter 2008-12-14 18:19 ` [PATCH 1/4] firewire: cdev: fix race of fw_device_op_release with bus reset Stefan Richter @ 2008-12-14 18:20 ` Stefan Richter 2008-12-18 22:43 ` Stefan Richter 2008-12-14 18:21 ` [PATCH 3/4] firewire: cdev: address handler input validation Stefan Richter 2008-12-14 18:21 ` [PATCH 4/4] firewire: core: remove outdated comment Stefan Richter 3 siblings, 1 reply; 9+ messages in thread From: Stefan Richter @ 2008-12-14 18:20 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason From: Jay Fenlason <fenlason@redhat.com> The current code uses a linked list and a counter for storing resources and the corresponding handle numbers. By changing to an idr we can be safe from counter wrap-around giving two resources the same handle. Furthermore, the deallocation ioctls now check whether the resource to be freed is of the intended type. Signed-off-by: Jay Fenlason <fenlason@redhat.com> Some rework by Stefan R: - The idr API documentation says we get an ID within 0...0x7fffffff. Hence we can rest assured that idr handles fit into cdev handles. - Fix some races. Add a client->in_shutdown flag for this purpose. - Add allocation retry to add_client_resource(). - It is possible to use idr_for_each() in fw_device_op_release(). - Small style changes. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/firewire/fw-cdev.c | 173 +++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 54 deletions(-) Index: linux/drivers/firewire/fw-cdev.c =================================================================== --- linux.orig/drivers/firewire/fw-cdev.c +++ linux/drivers/firewire/fw-cdev.c @@ -41,10 +41,12 @@ #include "fw-device.h" struct client; +struct client_resource; +typedef void (*client_resource_release_fn_t)(struct client *, + struct client_resource *); struct client_resource { - struct list_head link; - void (*release)(struct client *client, struct client_resource *r); - u32 handle; + client_resource_release_fn_t release; + int handle; }; /* @@ -78,9 +80,10 @@ struct iso_interrupt { struct client { u32 version; struct fw_device *device; + spinlock_t lock; - u32 resource_handle; - struct list_head resource_list; + bool in_shutdown; + struct idr resources; struct list_head event_list; wait_queue_head_t wait; u64 bus_reset_closure; @@ -126,9 +129,9 @@ static int fw_device_op_open(struct inod } client->device = device; - INIT_LIST_HEAD(&client->event_list); - INIT_LIST_HEAD(&client->resource_list); spin_lock_init(&client->lock); + idr_init(&client->resources); + INIT_LIST_HEAD(&client->event_list); init_waitqueue_head(&client->wait); file->private_data = client; @@ -151,7 +154,10 @@ static void queue_event(struct client *c event->v[1].size = size1; spin_lock_irqsave(&client->lock, flags); - list_add_tail(&event->link, &client->event_list); + if (client->in_shutdown) + kfree(event); + else + list_add_tail(&event->link, &client->event_list); spin_unlock_irqrestore(&client->lock, flags); wake_up_interruptible(&client->wait); @@ -310,34 +316,49 @@ static int ioctl_get_info(struct client return 0; } -static void -add_client_resource(struct client *client, struct client_resource *resource) +static int +add_client_resource(struct client *client, struct client_resource *resource, + gfp_t gfp_mask) { unsigned long flags; + int ret; + + retry: + if (idr_pre_get(&client->resources, gfp_mask) == 0) + return -ENOMEM; spin_lock_irqsave(&client->lock, flags); - list_add_tail(&resource->link, &client->resource_list); - resource->handle = client->resource_handle++; + if (client->in_shutdown) + ret = -ECANCELED; + else + ret = idr_get_new(&client->resources, resource, + &resource->handle); spin_unlock_irqrestore(&client->lock, flags); + + if (ret == -EAGAIN) + goto retry; + + return ret < 0 ? ret : 0; } static int release_client_resource(struct client *client, u32 handle, + client_resource_release_fn_t release, struct client_resource **resource) { struct client_resource *r; unsigned long flags; spin_lock_irqsave(&client->lock, flags); - list_for_each_entry(r, &client->resource_list, link) { - if (r->handle == handle) { - list_del(&r->link); - break; - } - } + if (client->in_shutdown) + r = NULL; + else + r = idr_find(&client->resources, handle); + if (r && r->release == release) + idr_remove(&client->resources, handle); spin_unlock_irqrestore(&client->lock, flags); - if (&r->link == &client->resource_list) + if (!(r && r->release == release)) return -EINVAL; if (resource) @@ -372,7 +393,12 @@ complete_transaction(struct fw_card *car memcpy(r->data, payload, r->length); spin_lock_irqsave(&client->lock, flags); - list_del(&response->resource.link); + /* + * If called while in shutdown, the idr tree must be left untouched. + * The idr handle will be removed later. + */ + if (!client->in_shutdown) + idr_remove(&client->resources, response->resource.handle); spin_unlock_irqrestore(&client->lock, flags); r->type = FW_CDEV_EVENT_RESPONSE; @@ -416,7 +442,7 @@ static int ioctl_send_request(struct cli copy_from_user(response->response.data, u64_to_uptr(request->data), request->length)) { ret = -EFAULT; - goto err; + goto failed; } switch (request->tcode) { @@ -434,11 +460,13 @@ static int ioctl_send_request(struct cli break; default: ret = -EINVAL; - goto err; + goto failed; } response->resource.release = release_transaction; - add_client_resource(client, &response->resource); + ret = add_client_resource(client, &response->resource, GFP_KERNEL); + if (ret < 0) + goto failed; fw_send_request(device->card, &response->transaction, request->tcode & 0x1f, @@ -453,7 +481,7 @@ static int ioctl_send_request(struct cli return sizeof(request) + request->length; else return sizeof(request); - err: + failed: kfree(response); return ret; @@ -500,22 +528,21 @@ handle_request(struct fw_card *card, str struct request *request; struct request_event *e; struct client *client = handler->client; + int ret; request = kmalloc(sizeof(*request), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (request == NULL || e == NULL) { - kfree(request); - kfree(e); - fw_send_response(card, r, RCODE_CONFLICT_ERROR); - return; - } + if (request == NULL || e == NULL) + goto failed; request->request = r; request->data = payload; request->length = length; request->resource.release = release_request; - add_client_resource(client, &request->resource); + ret = add_client_resource(client, &request->resource, GFP_ATOMIC); + if (ret < 0) + goto failed; e->request.type = FW_CDEV_EVENT_REQUEST; e->request.tcode = tcode; @@ -526,6 +553,12 @@ handle_request(struct fw_card *card, str queue_event(client, &e->event, &e->request, sizeof(e->request), payload, length); + return; + + failed: + kfree(request); + kfree(e); + fw_send_response(card, r, RCODE_CONFLICT_ERROR); } static void @@ -544,6 +577,7 @@ static int ioctl_allocate(struct client struct fw_cdev_allocate *request = buffer; struct address_handler *handler; struct fw_address_region region; + int ret; handler = kmalloc(sizeof(*handler), GFP_KERNEL); if (handler == NULL) @@ -563,7 +597,11 @@ static int ioctl_allocate(struct client } handler->resource.release = release_address_handler; - add_client_resource(client, &handler->resource); + ret = add_client_resource(client, &handler->resource, GFP_KERNEL); + if (ret < 0) { + release_address_handler(client, &handler->resource); + return ret; + } request->handle = handler->resource.handle; return 0; @@ -573,7 +611,8 @@ static int ioctl_deallocate(struct clien { struct fw_cdev_deallocate *request = buffer; - return release_client_resource(client, request->handle, NULL); + return release_client_resource(client, request->handle, + release_address_handler, NULL); } static int ioctl_send_response(struct client *client, void *buffer) @@ -582,8 +621,10 @@ static int ioctl_send_response(struct cl struct client_resource *resource; struct request *r; - if (release_client_resource(client, request->handle, &resource) < 0) + if (release_client_resource(client, request->handle, + release_transaction, &resource) < 0) return -EINVAL; + r = container_of(resource, struct request, resource); if (request->length < r->length) r->length = request->length; @@ -626,7 +667,7 @@ static int ioctl_add_descriptor(struct c { struct fw_cdev_add_descriptor *request = buffer; struct descriptor *descriptor; - int retval; + int ret; if (request->length > 256) return -EINVAL; @@ -638,8 +679,8 @@ static int ioctl_add_descriptor(struct c if (copy_from_user(descriptor->data, u64_to_uptr(request->data), request->length * 4)) { - kfree(descriptor); - return -EFAULT; + ret = -EFAULT; + goto failed; } descriptor->d.length = request->length; @@ -647,24 +688,31 @@ static int ioctl_add_descriptor(struct c descriptor->d.key = request->key; descriptor->d.data = descriptor->data; - retval = fw_core_add_descriptor(&descriptor->d); - if (retval < 0) { - kfree(descriptor); - return retval; - } + ret = fw_core_add_descriptor(&descriptor->d); + if (ret < 0) + goto failed; descriptor->resource.release = release_descriptor; - add_client_resource(client, &descriptor->resource); + ret = add_client_resource(client, &descriptor->resource, GFP_KERNEL); + if (ret < 0) { + fw_core_remove_descriptor(&descriptor->d); + goto failed; + } request->handle = descriptor->resource.handle; return 0; + failed: + kfree(descriptor); + + return ret; } static int ioctl_remove_descriptor(struct client *client, void *buffer) { struct fw_cdev_remove_descriptor *request = buffer; - return release_client_resource(client, request->handle, NULL); + return release_client_resource(client, request->handle, + release_descriptor, NULL); } static void @@ -1003,15 +1051,26 @@ static int fw_device_op_mmap(struct file return retval; } +static int shutdown_resource(int id, void *p, void *data) +{ + struct client *client = data; + struct client_resource *r = p; + + r->release(client, r); + + return 0; +} + static int fw_device_op_release(struct inode *inode, struct file *file) { struct client *client = file->private_data; + struct fw_device *device = client->device; struct event *e, *next_e; - struct client_resource *r, *next_r; + unsigned long flags; - mutex_lock(&client->device->client_list_mutex); + mutex_lock(&device->client_list_mutex); list_del(&client->link); - mutex_unlock(&client->device->client_list_mutex); + mutex_unlock(&device->client_list_mutex); if (client->buffer.pages) fw_iso_buffer_destroy(&client->buffer, client->device->card); @@ -1019,18 +1078,24 @@ static int fw_device_op_release(struct i if (client->iso_context) fw_iso_context_destroy(client->iso_context); - list_for_each_entry_safe(r, next_r, &client->resource_list, link) - r->release(client, r); + /* Freeze client->resources and client->event_list. */ + spin_lock_irqsave(&client->lock, flags); + client->in_shutdown = true; + spin_unlock_irqrestore(&client->lock, flags); - /* - * FIXME: We should wait for the async tasklets to stop - * running before freeing the memory. - */ + idr_for_each(&client->resources, shutdown_resource, client); + idr_remove_all(&client->resources); + idr_destroy(&client->resources); list_for_each_entry_safe(e, next_e, &client->event_list, link) kfree(e); - fw_device_put(client->device); + fw_device_put(device); + + /* + * FIXME: client should be reference-counted. It's extremely unlikely + * but there may still be transactions being completed at this point. + */ kfree(client); return 0; -- Stefan Richter -=====-==--- ==-- -===- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources 2008-12-14 18:20 ` [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources Stefan Richter @ 2008-12-18 22:43 ` Stefan Richter 2008-12-18 23:05 ` Stefan Richter 0 siblings, 1 reply; 9+ messages in thread From: Stefan Richter @ 2008-12-18 22:43 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason Stefan Richter wrote on 2008-12-14: > From: Jay Fenlason <fenlason@redhat.com> > > The current code uses a linked list and a counter for storing > resources and the corresponding handle numbers. By changing to an idr > we can be safe from counter wrap-around giving two resources the same > handle. > > Furthermore, the deallocation ioctls now check whether the resource to > be freed is of the intended type. > > Signed-off-by: Jay Fenlason <fenlason@redhat.com> > > Some rework by Stefan R: > - The idr API documentation says we get an ID within 0...0x7fffffff. > Hence we can rest assured that idr handles fit into cdev handles. > - Fix some races. Add a client->in_shutdown flag for this purpose. > - Add allocation retry to add_client_resource(). > - It is possible to use idr_for_each() in fw_device_op_release(). > - Small style changes. > > Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Back to the drawing board. This patch breaks FCP for dvgrab, kino, gscanbus. dvgrab: only works with -guid ... kino: shows live preview but can't list nor control camcorder gscanbus: segfaults if I click on the camera icon to bring up the info window with AV/C controls. -- Stefan Richter -=====-==--- ==-- =--=- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources 2008-12-18 22:43 ` Stefan Richter @ 2008-12-18 23:05 ` Stefan Richter 2008-12-21 15:47 ` [PATCH 2/4 update] " Stefan Richter 0 siblings, 1 reply; 9+ messages in thread From: Stefan Richter @ 2008-12-18 23:05 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason I wrote: > Back to the drawing board. This patch breaks FCP for dvgrab, kino, > gscanbus and testlibraw. Before: [...] - testing FCP monitoring on local node got fcp command from node 0 of 8 bytes: 01 23 45 67 89 ab cd ef got fcp response from node 0 of 8 bytes: 01 23 45 67 89 ab cd ef [...] After: [...] - testing FCP monitoring on local node [...] This is the only test of testlibraw which fails now. -- Stefan Richter -=====-==--- ==-- =--== http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/4 update] firewire: cdev: use an idr rather than a linked list for resources 2008-12-18 23:05 ` Stefan Richter @ 2008-12-21 15:47 ` Stefan Richter 0 siblings, 0 replies; 9+ messages in thread From: Stefan Richter @ 2008-12-21 15:47 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason From: Jay Fenlason <fenlason@redhat.com> The current code uses a linked list and a counter for storing resources and the corresponding handle numbers. By changing to an idr we can be safe from counter wrap-around giving two resources the same handle. Furthermore, the deallocation ioctls now check whether the resource to be freed is of the intended type. Signed-off-by: Jay Fenlason <fenlason@redhat.com> Some rework by Stefan R: - The idr API documentation says we get an ID within 0...0x7fffffff. Hence we can rest assured that idr handles fit into cdev handles. - Fix some races. Add a client->in_shutdown flag for this purpose. - Add allocation retry to add_client_resource(). - It is possible to use idr_for_each() in fw_device_op_release(). - Fix ioctl_send_response() regression. - Small style changes. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- FCP broke in the previous iteration of this patch because ioctl_send_response() specified a wrong release hook to release_client_resource(). drivers/firewire/fw-cdev.c | 165 +++++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 51 deletions(-) Index: linux/drivers/firewire/fw-cdev.c =================================================================== --- linux.orig/drivers/firewire/fw-cdev.c +++ linux/drivers/firewire/fw-cdev.c @@ -41,10 +41,12 @@ #include "fw-device.h" struct client; +struct client_resource; +typedef void (*client_resource_release_fn_t)(struct client *, + struct client_resource *); struct client_resource { - struct list_head link; - void (*release)(struct client *client, struct client_resource *r); - u32 handle; + client_resource_release_fn_t release; + int handle; }; /* @@ -78,9 +80,10 @@ struct iso_interrupt { struct client { u32 version; struct fw_device *device; + spinlock_t lock; - u32 resource_handle; - struct list_head resource_list; + bool in_shutdown; + struct idr resource_idr; struct list_head event_list; wait_queue_head_t wait; u64 bus_reset_closure; @@ -126,9 +129,9 @@ static int fw_device_op_open(struct inod } client->device = device; - INIT_LIST_HEAD(&client->event_list); - INIT_LIST_HEAD(&client->resource_list); spin_lock_init(&client->lock); + idr_init(&client->resource_idr); + INIT_LIST_HEAD(&client->event_list); init_waitqueue_head(&client->wait); file->private_data = client; @@ -151,7 +154,10 @@ static void queue_event(struct client *c event->v[1].size = size1; spin_lock_irqsave(&client->lock, flags); - list_add_tail(&event->link, &client->event_list); + if (client->in_shutdown) + kfree(event); + else + list_add_tail(&event->link, &client->event_list); spin_unlock_irqrestore(&client->lock, flags); wake_up_interruptible(&client->wait); @@ -310,34 +316,49 @@ static int ioctl_get_info(struct client return 0; } -static void -add_client_resource(struct client *client, struct client_resource *resource) +static int +add_client_resource(struct client *client, struct client_resource *resource, + gfp_t gfp_mask) { unsigned long flags; + int ret; + + retry: + if (idr_pre_get(&client->resource_idr, gfp_mask) == 0) + return -ENOMEM; spin_lock_irqsave(&client->lock, flags); - list_add_tail(&resource->link, &client->resource_list); - resource->handle = client->resource_handle++; + if (client->in_shutdown) + ret = -ECANCELED; + else + ret = idr_get_new(&client->resource_idr, resource, + &resource->handle); spin_unlock_irqrestore(&client->lock, flags); + + if (ret == -EAGAIN) + goto retry; + + return ret < 0 ? ret : 0; } static int release_client_resource(struct client *client, u32 handle, + client_resource_release_fn_t release, struct client_resource **resource) { struct client_resource *r; unsigned long flags; spin_lock_irqsave(&client->lock, flags); - list_for_each_entry(r, &client->resource_list, link) { - if (r->handle == handle) { - list_del(&r->link); - break; - } - } + if (client->in_shutdown) + r = NULL; + else + r = idr_find(&client->resource_idr, handle); + if (r && r->release == release) + idr_remove(&client->resource_idr, handle); spin_unlock_irqrestore(&client->lock, flags); - if (&r->link == &client->resource_list) + if (!(r && r->release == release)) return -EINVAL; if (resource) @@ -372,7 +393,12 @@ complete_transaction(struct fw_card *car memcpy(r->data, payload, r->length); spin_lock_irqsave(&client->lock, flags); - list_del(&response->resource.link); + /* + * If called while in shutdown, the idr tree must be left untouched. + * The idr handle will be removed later. + */ + if (!client->in_shutdown) + idr_remove(&client->resource_idr, response->resource.handle); spin_unlock_irqrestore(&client->lock, flags); r->type = FW_CDEV_EVENT_RESPONSE; @@ -416,7 +442,7 @@ static int ioctl_send_request(struct cli copy_from_user(response->response.data, u64_to_uptr(request->data), request->length)) { ret = -EFAULT; - goto err; + goto failed; } switch (request->tcode) { @@ -434,11 +460,13 @@ static int ioctl_send_request(struct cli break; default: ret = -EINVAL; - goto err; + goto failed; } response->resource.release = release_transaction; - add_client_resource(client, &response->resource); + ret = add_client_resource(client, &response->resource, GFP_KERNEL); + if (ret < 0) + goto failed; fw_send_request(device->card, &response->transaction, request->tcode & 0x1f, @@ -453,7 +481,7 @@ static int ioctl_send_request(struct cli return sizeof(request) + request->length; else return sizeof(request); - err: + failed: kfree(response); return ret; @@ -500,22 +528,21 @@ handle_request(struct fw_card *card, str struct request *request; struct request_event *e; struct client *client = handler->client; + int ret; request = kmalloc(sizeof(*request), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (request == NULL || e == NULL) { - kfree(request); - kfree(e); - fw_send_response(card, r, RCODE_CONFLICT_ERROR); - return; - } + if (request == NULL || e == NULL) + goto failed; request->request = r; request->data = payload; request->length = length; request->resource.release = release_request; - add_client_resource(client, &request->resource); + ret = add_client_resource(client, &request->resource, GFP_ATOMIC); + if (ret < 0) + goto failed; e->request.type = FW_CDEV_EVENT_REQUEST; e->request.tcode = tcode; @@ -526,6 +553,12 @@ handle_request(struct fw_card *card, str queue_event(client, &e->event, &e->request, sizeof(e->request), payload, length); + return; + + failed: + kfree(request); + kfree(e); + fw_send_response(card, r, RCODE_CONFLICT_ERROR); } static void @@ -544,6 +577,7 @@ static int ioctl_allocate(struct client struct fw_cdev_allocate *request = buffer; struct address_handler *handler; struct fw_address_region region; + int ret; handler = kmalloc(sizeof(*handler), GFP_KERNEL); if (handler == NULL) @@ -563,7 +597,11 @@ static int ioctl_allocate(struct client } handler->resource.release = release_address_handler; - add_client_resource(client, &handler->resource); + ret = add_client_resource(client, &handler->resource, GFP_KERNEL); + if (ret < 0) { + release_address_handler(client, &handler->resource); + return ret; + } request->handle = handler->resource.handle; return 0; @@ -573,7 +611,8 @@ static int ioctl_deallocate(struct clien { struct fw_cdev_deallocate *request = buffer; - return release_client_resource(client, request->handle, NULL); + return release_client_resource(client, request->handle, + release_address_handler, NULL); } static int ioctl_send_response(struct client *client, void *buffer) @@ -582,8 +621,10 @@ static int ioctl_send_response(struct cl struct client_resource *resource; struct request *r; - if (release_client_resource(client, request->handle, &resource) < 0) + if (release_client_resource(client, request->handle, + release_request, &resource) < 0) return -EINVAL; + r = container_of(resource, struct request, resource); if (request->length < r->length) r->length = request->length; @@ -626,7 +667,7 @@ static int ioctl_add_descriptor(struct c { struct fw_cdev_add_descriptor *request = buffer; struct descriptor *descriptor; - int retval; + int ret; if (request->length > 256) return -EINVAL; @@ -638,8 +679,8 @@ static int ioctl_add_descriptor(struct c if (copy_from_user(descriptor->data, u64_to_uptr(request->data), request->length * 4)) { - kfree(descriptor); - return -EFAULT; + ret = -EFAULT; + goto failed; } descriptor->d.length = request->length; @@ -647,24 +688,31 @@ static int ioctl_add_descriptor(struct c descriptor->d.key = request->key; descriptor->d.data = descriptor->data; - retval = fw_core_add_descriptor(&descriptor->d); - if (retval < 0) { - kfree(descriptor); - return retval; - } + ret = fw_core_add_descriptor(&descriptor->d); + if (ret < 0) + goto failed; descriptor->resource.release = release_descriptor; - add_client_resource(client, &descriptor->resource); + ret = add_client_resource(client, &descriptor->resource, GFP_KERNEL); + if (ret < 0) { + fw_core_remove_descriptor(&descriptor->d); + goto failed; + } request->handle = descriptor->resource.handle; return 0; + failed: + kfree(descriptor); + + return ret; } static int ioctl_remove_descriptor(struct client *client, void *buffer) { struct fw_cdev_remove_descriptor *request = buffer; - return release_client_resource(client, request->handle, NULL); + return release_client_resource(client, request->handle, + release_descriptor, NULL); } static void @@ -1003,11 +1051,21 @@ static int fw_device_op_mmap(struct file return retval; } +static int shutdown_resource(int id, void *p, void *data) +{ + struct client_resource *r = p; + struct client *client = data; + + r->release(client, r); + + return 0; +} + static int fw_device_op_release(struct inode *inode, struct file *file) { struct client *client = file->private_data; struct event *e, *next_e; - struct client_resource *r, *next_r; + unsigned long flags; mutex_lock(&client->device->client_list_mutex); list_del(&client->link); @@ -1019,17 +1077,22 @@ static int fw_device_op_release(struct i if (client->iso_context) fw_iso_context_destroy(client->iso_context); - list_for_each_entry_safe(r, next_r, &client->resource_list, link) - r->release(client, r); + /* Freeze client->resource_idr and client->event_list */ + spin_lock_irqsave(&client->lock, flags); + client->in_shutdown = true; + spin_unlock_irqrestore(&client->lock, flags); - /* - * FIXME: We should wait for the async tasklets to stop - * running before freeing the memory. - */ + idr_for_each(&client->resource_idr, shutdown_resource, client); + idr_remove_all(&client->resource_idr); + idr_destroy(&client->resource_idr); list_for_each_entry_safe(e, next_e, &client->event_list, link) kfree(e); + /* + * FIXME: client should be reference-counted. It's extremely unlikely + * but there may still be transactions being completed at this point. + */ fw_device_put(client->device); kfree(client); -- Stefan Richter -=====-==--- ==-- =-=-= http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/4] firewire: cdev: address handler input validation 2008-12-14 18:17 ` [PATCH 0/4] firewire: cdev resources and events Stefan Richter 2008-12-14 18:19 ` [PATCH 1/4] firewire: cdev: fix race of fw_device_op_release with bus reset Stefan Richter 2008-12-14 18:20 ` [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources Stefan Richter @ 2008-12-14 18:21 ` Stefan Richter 2008-12-14 18:21 ` [PATCH 4/4] firewire: core: remove outdated comment Stefan Richter 3 siblings, 0 replies; 9+ messages in thread From: Stefan Richter @ 2008-12-14 18:21 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason Like before my commit 1415d9189e8c59aa9c77a3bba419dcea062c145f, fw_core_add_address_handler() does not align the address region now. Instead the caller is required to pass valid parameters. Since one of the callers of fw_core_add_address_handler() is the cdev userspace interface, we now check for valid input. If the client is buggy, we give it a hint with -EINVAL. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/firewire/fw-cdev.c | 5 +++-- drivers/firewire/fw-transaction.c | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) Index: linux/drivers/firewire/fw-cdev.c =================================================================== --- linux.orig/drivers/firewire/fw-cdev.c +++ linux/drivers/firewire/fw-cdev.c @@ -591,9 +591,10 @@ static int ioctl_allocate(struct client handler->closure = request->closure; handler->client = client; - if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) { + ret = fw_core_add_address_handler(&handler->handler, ®ion); + if (ret < 0) { kfree(handler); - return -EBUSY; + return ret; } handler->resource.release = release_address_handler; Index: linux/drivers/firewire/fw-transaction.c =================================================================== --- linux.orig/drivers/firewire/fw-transaction.c +++ linux/drivers/firewire/fw-transaction.c @@ -449,16 +449,19 @@ const struct fw_address_region fw_unit_s #endif /* 0 */ /** - * Allocate a range of addresses in the node space of the OHCI - * controller. When a request is received that falls within the - * specified address range, the specified callback is invoked. The - * parameters passed to the callback give the details of the - * particular request. + * fw_core_add_address_handler - register for incoming requests + * @handler: callback + * @region: region in the IEEE 1212 node space address range + * + * region->start, ->end, and handler->length have to be quadlet-aligned. + * + * When a request is received that falls within the specified address range, + * the specified callback is invoked. The parameters passed to the callback + * give the details of the particular request. * * Return value: 0 on success, non-zero otherwise. * The start offset of the handler's address region is determined by * fw_core_add_address_handler() and is returned in handler->offset. - * The offset is quadlet-aligned. */ int fw_core_add_address_handler(struct fw_address_handler *handler, @@ -468,17 +471,23 @@ fw_core_add_address_handler(struct fw_ad unsigned long flags; int ret = -EBUSY; + if (region->start & 0xffff000000000003ULL || + region->end & 0xffff000000000003ULL || + region->start >= region->end || + handler->length & 3 || + handler->length == 0) + return -EINVAL; + spin_lock_irqsave(&address_handler_lock, flags); - handler->offset = roundup(region->start, 4); + handler->offset = region->start; while (handler->offset + handler->length <= region->end) { other = lookup_overlapping_address_handler(&address_handler_list, handler->offset, handler->length); if (other != NULL) { - handler->offset = - roundup(other->offset + other->length, 4); + handler->offset += other->length; } else { list_add_tail(&handler->link, &address_handler_list); ret = 0; -- Stefan Richter -=====-==--- ==-- -===- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/4] firewire: core: remove outdated comment 2008-12-14 18:17 ` [PATCH 0/4] firewire: cdev resources and events Stefan Richter ` (2 preceding siblings ...) 2008-12-14 18:21 ` [PATCH 3/4] firewire: cdev: address handler input validation Stefan Richter @ 2008-12-14 18:21 ` Stefan Richter 3 siblings, 0 replies; 9+ messages in thread From: Stefan Richter @ 2008-12-14 18:21 UTC (permalink / raw) To: linux1394-devel; +Cc: linux-kernel, Jay Fenlason Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> --- drivers/firewire/fw-transaction.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) Index: linux/drivers/firewire/fw-transaction.c =================================================================== --- linux.orig/drivers/firewire/fw-transaction.c +++ linux/drivers/firewire/fw-transaction.c @@ -502,12 +502,7 @@ fw_core_add_address_handler(struct fw_ad EXPORT_SYMBOL(fw_core_add_address_handler); /** - * Deallocate a range of addresses allocated with fw_allocate. This - * will call the associated callback one last time with a the special - * tcode TCODE_DEALLOCATE, to let the client destroy the registered - * callback data. For convenience, the callback parameters offset and - * length are set to the start and the length respectively for the - * deallocated region, payload is set to NULL. + * fw_core_remove_address_handler - unregister an address handler */ void fw_core_remove_address_handler(struct fw_address_handler *handler) { -- Stefan Richter -=====-==--- ==-- -===- http://arcgraph.de/sr/ ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-12-21 15:48 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20081124163728.GA5826@redhat.com>
2008-11-24 19:09 ` idr: IDs guaranteed to be less than 1<<31? (was Re: [PATCH] firewire: us an idr rather than a linked list for resources) Stefan Richter
[not found] ` <492AEED8.9030102@s5r6.in-berlin.de>
[not found] ` <492AEF9D.5010307@s5r6.in-berlin.de>
2008-12-14 18:17 ` [PATCH 0/4] firewire: cdev resources and events Stefan Richter
2008-12-14 18:19 ` [PATCH 1/4] firewire: cdev: fix race of fw_device_op_release with bus reset Stefan Richter
2008-12-14 18:20 ` [PATCH 2/4] firewire: cdev: use an idr rather than a linked list for resources Stefan Richter
2008-12-18 22:43 ` Stefan Richter
2008-12-18 23:05 ` Stefan Richter
2008-12-21 15:47 ` [PATCH 2/4 update] " Stefan Richter
2008-12-14 18:21 ` [PATCH 3/4] firewire: cdev: address handler input validation Stefan Richter
2008-12-14 18:21 ` [PATCH 4/4] firewire: core: remove outdated comment Stefan Richter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox