* 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
* [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
* [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
* 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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.