summaryrefslogtreecommitdiffstats
path: root/drivers/firewire/core-transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/core-transaction.c')
-rw-r--r--drivers/firewire/core-transaction.c33
1 files changed, 29 insertions, 4 deletions
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);