diff options
-rw-r--r-- | drivers/firewire/core-cdev.c | 4 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 33 | ||||
-rw-r--r-- | drivers/firewire/core.h | 3 |
3 files changed, 34 insertions, 6 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 958aa4662ccb..93dd80d8d4e1 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -826,12 +826,12 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) if (a->length != fw_get_response_length(r->request)) { ret = -EINVAL; - kfree(r->request); + fw_request_put(r->request); goto out; } if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { ret = -EFAULT; - kfree(r->request); + fw_request_put(r->request); goto out; } fw_send_response(r->card, r->request, a->rcode); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index af498d767702..83f61cf1aa8f 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -617,6 +617,7 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler) EXPORT_SYMBOL(fw_core_remove_address_handler); struct fw_request { + struct kref kref; struct fw_packet response; u32 request_header[4]; int ack; @@ -625,13 +626,33 @@ struct fw_request { u32 data[]; }; +void fw_request_get(struct fw_request *request) +{ + kref_get(&request->kref); +} + +static void release_request(struct kref *kref) +{ + struct fw_request *request = container_of(kref, struct fw_request, kref); + + kfree(request); +} + +void fw_request_put(struct fw_request *request) +{ + kref_put(&request->kref, release_request); +} + static void free_response_callback(struct fw_packet *packet, struct fw_card *card, int status) { - struct fw_request *request; + struct fw_request *request = container_of(packet, struct fw_request, response); - request = container_of(packet, struct fw_request, response); - kfree(request); + // Decrease the reference count since not at in-flight. + fw_request_put(request); + + // Decrease the reference count to release the object. + fw_request_put(request); } int fw_get_response_length(struct fw_request *r) @@ -782,6 +803,7 @@ static struct fw_request *allocate_request(struct fw_card *card, request = kmalloc(sizeof(*request) + length, GFP_ATOMIC); if (request == NULL) return NULL; + kref_init(&request->kref); request->response.speed = p->speed; request->response.timestamp = @@ -809,7 +831,7 @@ void fw_send_response(struct fw_card *card, /* unified transaction or broadcast transaction: don't respond */ if (request->ack != ACK_PENDING || HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { - kfree(request); + fw_request_put(request); return; } @@ -821,6 +843,9 @@ void fw_send_response(struct fw_card *card, fw_fill_response(&request->response, request->request_header, rcode, NULL, 0); + // Increase the reference count so that the object is kept during in-flight. + fw_request_get(request); + card->driver->send_response(card, &request->response); } EXPORT_SYMBOL(fw_send_response); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 71d5f16f311c..78c99f1d27fa 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -244,6 +244,9 @@ int fw_get_response_length(struct fw_request *request); void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length); +void fw_request_get(struct fw_request *request); +void fw_request_put(struct fw_request *request); + #define FW_PHY_CONFIG_NO_NODE_ID -1 #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, |