diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-07-22 15:35:47 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-07-25 14:10:32 -0400 |
commit | c0220d686b926a5865a2032c805015758bfdda69 (patch) | |
tree | 2b1dd1177e9e24ccea13c517054e78c5abc2eee0 /drivers/firewire/fw-transaction.c | |
parent | 95984f62c9b0bf6d89ef4f514b1afe73623481de (diff) |
firewire: avoid memleak after phy config transmit failure
Use only statically allocated data for PHY config packet transmission.
With the previous incarnation, some data wouldn't be freed if the packet
transmit callback was never called.
A theoretical drawback now is that, in PCs with more than one card,
card A may complete() for a waiter on card B. But this is highly
unlikely and its impact not serious. Bus manager B may reset bus B
before the PHY config went out, but the next phy config on B should be
fine. However, with a timeout of 100ms, this situation is close to
impossible.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-transaction.c')
-rw-r--r-- | drivers/firewire/fw-transaction.c | 58 |
1 files changed, 21 insertions, 37 deletions
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 861dd60de7d9..e5d1a0b64fcf 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/kref.h> | 23 | #include <linux/kref.h> |
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/mutex.h> | ||
25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
26 | #include <linux/interrupt.h> | 27 | #include <linux/interrupt.h> |
27 | #include <linux/pci.h> | 28 | #include <linux/pci.h> |
@@ -295,58 +296,41 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, | |||
295 | } | 296 | } |
296 | EXPORT_SYMBOL(fw_send_request); | 297 | EXPORT_SYMBOL(fw_send_request); |
297 | 298 | ||
298 | struct fw_phy_packet { | 299 | static DEFINE_MUTEX(phy_config_mutex); |
299 | struct fw_packet packet; | 300 | static DECLARE_COMPLETION(phy_config_done); |
300 | struct completion done; | ||
301 | struct kref kref; | ||
302 | }; | ||
303 | |||
304 | static void phy_packet_release(struct kref *kref) | ||
305 | { | ||
306 | struct fw_phy_packet *p = | ||
307 | container_of(kref, struct fw_phy_packet, kref); | ||
308 | kfree(p); | ||
309 | } | ||
310 | 301 | ||
311 | static void transmit_phy_packet_callback(struct fw_packet *packet, | 302 | static void transmit_phy_packet_callback(struct fw_packet *packet, |
312 | struct fw_card *card, int status) | 303 | struct fw_card *card, int status) |
313 | { | 304 | { |
314 | struct fw_phy_packet *p = | 305 | complete(&phy_config_done); |
315 | container_of(packet, struct fw_phy_packet, packet); | ||
316 | |||
317 | complete(&p->done); | ||
318 | kref_put(&p->kref, phy_packet_release); | ||
319 | } | 306 | } |
320 | 307 | ||
308 | static struct fw_packet phy_config_packet = { | ||
309 | .header_length = 8, | ||
310 | .payload_length = 0, | ||
311 | .speed = SCODE_100, | ||
312 | .callback = transmit_phy_packet_callback, | ||
313 | }; | ||
314 | |||
321 | void fw_send_phy_config(struct fw_card *card, | 315 | void fw_send_phy_config(struct fw_card *card, |
322 | int node_id, int generation, int gap_count) | 316 | int node_id, int generation, int gap_count) |
323 | { | 317 | { |
324 | struct fw_phy_packet *p; | ||
325 | long timeout = DIV_ROUND_UP(HZ, 10); | 318 | long timeout = DIV_ROUND_UP(HZ, 10); |
326 | u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | | 319 | u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | |
327 | PHY_CONFIG_ROOT_ID(node_id) | | 320 | PHY_CONFIG_ROOT_ID(node_id) | |
328 | PHY_CONFIG_GAP_COUNT(gap_count); | 321 | PHY_CONFIG_GAP_COUNT(gap_count); |
329 | 322 | ||
330 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 323 | mutex_lock(&phy_config_mutex); |
331 | if (p == NULL) | 324 | |
332 | return; | 325 | phy_config_packet.header[0] = data; |
326 | phy_config_packet.header[1] = ~data; | ||
327 | phy_config_packet.generation = generation; | ||
328 | INIT_COMPLETION(phy_config_done); | ||
329 | |||
330 | card->driver->send_request(card, &phy_config_packet); | ||
331 | wait_for_completion_timeout(&phy_config_done, timeout); | ||
333 | 332 | ||
334 | p->packet.header[0] = data; | 333 | mutex_unlock(&phy_config_mutex); |
335 | p->packet.header[1] = ~data; | ||
336 | p->packet.header_length = 8; | ||
337 | p->packet.payload_length = 0; | ||
338 | p->packet.speed = SCODE_100; | ||
339 | p->packet.generation = generation; | ||
340 | p->packet.callback = transmit_phy_packet_callback; | ||
341 | init_completion(&p->done); | ||
342 | kref_set(&p->kref, 2); | ||
343 | |||
344 | card->driver->send_request(card, &p->packet); | ||
345 | timeout = wait_for_completion_timeout(&p->done, timeout); | ||
346 | kref_put(&p->kref, phy_packet_release); | ||
347 | |||
348 | /* will leak p if the callback is never executed */ | ||
349 | WARN_ON(timeout == 0); | ||
350 | } | 334 | } |
351 | 335 | ||
352 | void fw_flush_transactions(struct fw_card *card) | 336 | void fw_flush_transactions(struct fw_card *card) |