Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6:
  firewire: fw-sbp2: fix races
  firewire: fw-sbp2: delay first login to avoid retries
  firewire: fw-ohci: initialization failure path fixes
  firewire: fw-ohci: don't leak dma memory on module removal
  firewire: fix struct fw_node memory leak
  firewire: Survive more than 256 bus resets
This commit is contained in:
Linus Torvalds 2008-10-27 09:37:16 -07:00
commit 1d63e72640
4 changed files with 73 additions and 23 deletions

View file

@ -476,6 +476,7 @@ static int ar_context_add_page(struct ar_context *ctx)
if (ab == NULL) if (ab == NULL)
return -ENOMEM; return -ENOMEM;
ab->next = NULL;
memset(&ab->descriptor, 0, sizeof(ab->descriptor)); memset(&ab->descriptor, 0, sizeof(ab->descriptor));
ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
DESCRIPTOR_STATUS | DESCRIPTOR_STATUS |
@ -496,6 +497,21 @@ static int ar_context_add_page(struct ar_context *ctx)
return 0; return 0;
} }
static void ar_context_release(struct ar_context *ctx)
{
struct ar_buffer *ab, *ab_next;
size_t offset;
dma_addr_t ab_bus;
for (ab = ctx->current_buffer; ab; ab = ab_next) {
ab_next = ab->next;
offset = offsetof(struct ar_buffer, data);
ab_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
dma_free_coherent(ctx->ohci->card.device, PAGE_SIZE,
ab, ab_bus);
}
}
#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) #if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
#define cond_le32_to_cpu(v) \ #define cond_le32_to_cpu(v) \
(ohci->old_uninorth ? (__force __u32)(v) : le32_to_cpu(v)) (ohci->old_uninorth ? (__force __u32)(v) : le32_to_cpu(v))
@ -2349,8 +2365,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
ohci = kzalloc(sizeof(*ohci), GFP_KERNEL); ohci = kzalloc(sizeof(*ohci), GFP_KERNEL);
if (ohci == NULL) { if (ohci == NULL) {
fw_error("Could not malloc fw_ohci data.\n"); err = -ENOMEM;
return -ENOMEM; goto fail;
} }
fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev); fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev);
@ -2359,7 +2375,7 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
err = pci_enable_device(dev); err = pci_enable_device(dev);
if (err) { if (err) {
fw_error("Failed to enable OHCI hardware.\n"); fw_error("Failed to enable OHCI hardware\n");
goto fail_free; goto fail_free;
} }
@ -2427,9 +2443,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
ohci->ir_context_list = kzalloc(size, GFP_KERNEL); ohci->ir_context_list = kzalloc(size, GFP_KERNEL);
if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) { if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) {
fw_error("Out of memory for it/ir contexts.\n");
err = -ENOMEM; err = -ENOMEM;
goto fail_registers; goto fail_contexts;
} }
/* self-id dma buffer allocation */ /* self-id dma buffer allocation */
@ -2438,9 +2453,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
&ohci->self_id_bus, &ohci->self_id_bus,
GFP_KERNEL); GFP_KERNEL);
if (ohci->self_id_cpu == NULL) { if (ohci->self_id_cpu == NULL) {
fw_error("Out of memory for self ID buffer.\n");
err = -ENOMEM; err = -ENOMEM;
goto fail_registers; goto fail_contexts;
} }
bus_options = reg_read(ohci, OHCI1394_BusOptions); bus_options = reg_read(ohci, OHCI1394_BusOptions);
@ -2460,9 +2474,13 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
fail_self_id: fail_self_id:
dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE, dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE,
ohci->self_id_cpu, ohci->self_id_bus); ohci->self_id_cpu, ohci->self_id_bus);
fail_registers: fail_contexts:
kfree(ohci->it_context_list);
kfree(ohci->ir_context_list); kfree(ohci->ir_context_list);
kfree(ohci->it_context_list);
context_release(&ohci->at_response_ctx);
context_release(&ohci->at_request_ctx);
ar_context_release(&ohci->ar_response_ctx);
ar_context_release(&ohci->ar_request_ctx);
pci_iounmap(dev, ohci->registers); pci_iounmap(dev, ohci->registers);
fail_iomem: fail_iomem:
pci_release_region(dev, 0); pci_release_region(dev, 0);
@ -2471,6 +2489,9 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
fail_free: fail_free:
kfree(&ohci->card); kfree(&ohci->card);
ohci_pmac_off(dev); ohci_pmac_off(dev);
fail:
if (err == -ENOMEM)
fw_error("Out of memory\n");
return err; return err;
} }
@ -2491,8 +2512,19 @@ static void pci_remove(struct pci_dev *dev)
software_reset(ohci); software_reset(ohci);
free_irq(dev->irq, ohci); free_irq(dev->irq, ohci);
if (ohci->next_config_rom && ohci->next_config_rom != ohci->config_rom)
dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
ohci->next_config_rom, ohci->next_config_rom_bus);
if (ohci->config_rom)
dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
ohci->config_rom, ohci->config_rom_bus);
dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE, dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE,
ohci->self_id_cpu, ohci->self_id_bus); ohci->self_id_cpu, ohci->self_id_bus);
ar_context_release(&ohci->ar_request_ctx);
ar_context_release(&ohci->ar_response_ctx);
context_release(&ohci->at_request_ctx);
context_release(&ohci->at_response_ctx);
kfree(ohci->it_context_list); kfree(ohci->it_context_list);
kfree(ohci->ir_context_list); kfree(ohci->ir_context_list);
pci_iounmap(dev, ohci->registers); pci_iounmap(dev, ohci->registers);

View file

@ -173,6 +173,9 @@ struct sbp2_target {
int blocked; /* ditto */ int blocked; /* ditto */
}; };
/* Impossible login_id, to detect logout attempt before successful login */
#define INVALID_LOGIN_ID 0x10000
/* /*
* Per section 7.4.8 of the SBP-2 spec, a mgt_ORB_timeout value can be * Per section 7.4.8 of the SBP-2 spec, a mgt_ORB_timeout value can be
* provided in the config rom. Most devices do provide a value, which * provided in the config rom. Most devices do provide a value, which
@ -788,9 +791,20 @@ static void sbp2_release_target(struct kref *kref)
scsi_remove_device(sdev); scsi_remove_device(sdev);
scsi_device_put(sdev); scsi_device_put(sdev);
} }
sbp2_send_management_orb(lu, tgt->node_id, lu->generation, if (lu->login_id != INVALID_LOGIN_ID) {
SBP2_LOGOUT_REQUEST, lu->login_id, NULL); int generation, node_id;
/*
* tgt->node_id may be obsolete here if we failed
* during initial login or after a bus reset where
* the topology changed.
*/
generation = device->generation;
smp_rmb(); /* node_id vs. generation */
node_id = device->node_id;
sbp2_send_management_orb(lu, node_id, generation,
SBP2_LOGOUT_REQUEST,
lu->login_id, NULL);
}
fw_core_remove_address_handler(&lu->address_handler); fw_core_remove_address_handler(&lu->address_handler);
list_del(&lu->link); list_del(&lu->link);
kfree(lu); kfree(lu);
@ -805,19 +819,20 @@ static void sbp2_release_target(struct kref *kref)
static struct workqueue_struct *sbp2_wq; static struct workqueue_struct *sbp2_wq;
static void sbp2_target_put(struct sbp2_target *tgt)
{
kref_put(&tgt->kref, sbp2_release_target);
}
/* /*
* Always get the target's kref when scheduling work on one its units. * Always get the target's kref when scheduling work on one its units.
* Each workqueue job is responsible to call sbp2_target_put() upon return. * Each workqueue job is responsible to call sbp2_target_put() upon return.
*/ */
static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
{ {
if (queue_delayed_work(sbp2_wq, &lu->work, delay)) kref_get(&lu->tgt->kref);
kref_get(&lu->tgt->kref); if (!queue_delayed_work(sbp2_wq, &lu->work, delay))
} sbp2_target_put(lu->tgt);
static void sbp2_target_put(struct sbp2_target *tgt)
{
kref_put(&tgt->kref, sbp2_release_target);
} }
/* /*
@ -978,6 +993,7 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
lu->tgt = tgt; lu->tgt = tgt;
lu->lun = lun_entry & 0xffff; lu->lun = lun_entry & 0xffff;
lu->login_id = INVALID_LOGIN_ID;
lu->retries = 0; lu->retries = 0;
lu->has_sdev = false; lu->has_sdev = false;
lu->blocked = false; lu->blocked = false;
@ -1147,7 +1163,7 @@ static int sbp2_probe(struct device *dev)
/* Do the login in a workqueue so we can easily reschedule retries. */ /* Do the login in a workqueue so we can easily reschedule retries. */
list_for_each_entry(lu, &tgt->lu_list, link) list_for_each_entry(lu, &tgt->lu_list, link)
sbp2_queue_work(lu, 0); sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
return 0; return 0;
fail_tgt_put: fail_tgt_put:

View file

@ -413,7 +413,7 @@ static void
update_tree(struct fw_card *card, struct fw_node *root) update_tree(struct fw_card *card, struct fw_node *root)
{ {
struct list_head list0, list1; struct list_head list0, list1;
struct fw_node *node0, *node1; struct fw_node *node0, *node1, *next1;
int i, event; int i, event;
INIT_LIST_HEAD(&list0); INIT_LIST_HEAD(&list0);
@ -485,7 +485,9 @@ update_tree(struct fw_card *card, struct fw_node *root)
} }
node0 = fw_node(node0->link.next); node0 = fw_node(node0->link.next);
node1 = fw_node(node1->link.next); next1 = fw_node(node1->link.next);
fw_node_put(node1);
node1 = next1;
} }
} }

View file

@ -248,7 +248,7 @@ struct fw_card {
struct fw_node *local_node; struct fw_node *local_node;
struct fw_node *root_node; struct fw_node *root_node;
struct fw_node *irm_node; struct fw_node *irm_node;
int color; u8 color; /* must be u8 to match the definition in struct fw_node */
int gap_count; int gap_count;
bool beta_repeaters_present; bool beta_repeaters_present;