diff options
author | Thomas Klein <osstklei@de.ibm.com> | 2008-02-13 16:18:33 +0100 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2008-02-23 23:54:59 -0500 |
commit | 21eee2dd1b5702f15924f18f923b2a281f0e72e8 (patch) | |
tree | 451ef826604048c9b3fd0d2362bbc9659170da6a /drivers/net/ehea/ehea_main.c | |
parent | 8d3c202be23c5a915f7053ebd4e96f44700c6a62 (diff) |
ehea: add kdump support
This patch adds kdump support to the ehea driver. As the firmware doesn't free
resource handles automatically, the driver has to run an as simple as possible
free resource function in case of a crash shutdown. The function iterates over
two arrays freeing all resource handles which are stored there. The arrays are
kept up-to-date during normal runtime. The crash handler fn is triggered by the
recently introduced PPC crash shutdown reg/unreg functions.
Signed-off-by: Thomas Klein <tklein@de.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/ehea/ehea_main.c')
-rw-r--r-- | drivers/net/ehea/ehea_main.c | 281 |
1 files changed, 257 insertions, 24 deletions
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index c051c7e09b9..21af674b764 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -35,6 +35,7 @@ #include <linux/if_ether.h> #include <linux/notifier.h> #include <linux/reboot.h> +#include <asm/kexec.h> #include <net/ip.h> @@ -98,8 +99,10 @@ static int port_name_cnt; static LIST_HEAD(adapter_list); u64 ehea_driver_flags; struct work_struct ehea_rereg_mr_task; - struct semaphore dlpar_mem_lock; +struct ehea_fw_handle_array ehea_fw_handles; +struct ehea_bcmc_reg_array ehea_bcmc_regs; + static int __devinit ehea_probe_adapter(struct of_device *dev, const struct of_device_id *id); @@ -132,6 +135,160 @@ void ehea_dump(void *adr, int len, char *msg) } } +static void ehea_update_firmware_handles(void) +{ + struct ehea_fw_handle_entry *arr = NULL; + struct ehea_adapter *adapter; + int num_adapters = 0; + int num_ports = 0; + int num_portres = 0; + int i = 0; + int num_fw_handles, k, l; + + /* Determine number of handles */ + list_for_each_entry(adapter, &adapter_list, list) { + num_adapters++; + + for (k = 0; k < EHEA_MAX_PORTS; k++) { + struct ehea_port *port = adapter->port[k]; + + if (!port || (port->state != EHEA_PORT_UP)) + continue; + + num_ports++; + num_portres += port->num_def_qps + port->num_add_tx_qps; + } + } + + num_fw_handles = num_adapters * EHEA_NUM_ADAPTER_FW_HANDLES + + num_ports * EHEA_NUM_PORT_FW_HANDLES + + num_portres * EHEA_NUM_PORTRES_FW_HANDLES; + + if (num_fw_handles) { + arr = kzalloc(num_fw_handles * sizeof(*arr), GFP_KERNEL); + if (!arr) + return; /* Keep the existing array */ + } else + goto out_update; + + list_for_each_entry(adapter, &adapter_list, list) { + for (k = 0; k < EHEA_MAX_PORTS; k++) { + struct ehea_port *port = adapter->port[k]; + + if (!port || (port->state != EHEA_PORT_UP)) + continue; + + for (l = 0; + l < port->num_def_qps + port->num_add_tx_qps; + l++) { + struct ehea_port_res *pr = &port->port_res[l]; + + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->qp->fw_handle; + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->send_cq->fw_handle; + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->recv_cq->fw_handle; + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->eq->fw_handle; + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->send_mr.handle; + arr[i].adh = adapter->handle; + arr[i++].fwh = pr->recv_mr.handle; + } + arr[i].adh = adapter->handle; + arr[i++].fwh = port->qp_eq->fw_handle; + } + + arr[i].adh = adapter->handle; + arr[i++].fwh = adapter->neq->fw_handle; + + if (adapter->mr.handle) { + arr[i].adh = adapter->handle; + arr[i++].fwh = adapter->mr.handle; + } + } + +out_update: + kfree(ehea_fw_handles.arr); + ehea_fw_handles.arr = arr; + ehea_fw_handles.num_entries = i; +} + +static void ehea_update_bcmc_registrations(void) +{ + struct ehea_bcmc_reg_entry *arr = NULL; + struct ehea_adapter *adapter; + struct ehea_mc_list *mc_entry; + int num_registrations = 0; + int i = 0; + int k; + + /* Determine number of registrations */ + list_for_each_entry(adapter, &adapter_list, list) + for (k = 0; k < EHEA_MAX_PORTS; k++) { + struct ehea_port *port = adapter->port[k]; + + if (!port || (port->state != EHEA_PORT_UP)) + continue; + + num_registrations += 2; /* Broadcast registrations */ + + list_for_each_entry(mc_entry, &port->mc_list->list,list) + num_registrations += 2; + } + + if (num_registrations) { + arr = kzalloc(num_registrations * sizeof(*arr), GFP_KERNEL); + if (!arr) + return; /* Keep the existing array */ + } else + goto out_update; + + list_for_each_entry(adapter, &adapter_list, list) { + for (k = 0; k < EHEA_MAX_PORTS; k++) { + struct ehea_port *port = adapter->port[k]; + + if (!port || (port->state != EHEA_PORT_UP)) + continue; + + arr[i].adh = adapter->handle; + arr[i].port_id = port->logical_port_id; + arr[i].reg_type = EHEA_BCMC_BROADCAST | + EHEA_BCMC_UNTAGGED; + arr[i++].macaddr = port->mac_addr; + + arr[i].adh = adapter->handle; + arr[i].port_id = port->logical_port_id; + arr[i].reg_type = EHEA_BCMC_BROADCAST | + EHEA_BCMC_VLANID_ALL; + arr[i++].macaddr = port->mac_addr; + + list_for_each_entry(mc_entry, + &port->mc_list->list, list) { + arr[i].adh = adapter->handle; + arr[i].port_id = port->logical_port_id; + arr[i].reg_type = EHEA_BCMC_SCOPE_ALL | + EHEA_BCMC_MULTICAST | + EHEA_BCMC_UNTAGGED; + arr[i++].macaddr = mc_entry->macaddr; + + arr[i].adh = adapter->handle; + arr[i].port_id = port->logical_port_id; + arr[i].reg_type = EHEA_BCMC_SCOPE_ALL | + EHEA_BCMC_MULTICAST | + EHEA_BCMC_VLANID_ALL; + arr[i++].macaddr = mc_entry->macaddr; + } + } + } + +out_update: + kfree(ehea_bcmc_regs.arr); + ehea_bcmc_regs.arr = arr; + ehea_bcmc_regs.num_entries = i; +} + static struct net_device_stats *ehea_get_stats(struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); @@ -1601,19 +1758,25 @@ static int ehea_set_mac_addr(struct net_device *dev, void *sa) memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len); + down(&ehea_bcmc_regs.lock); + /* Deregister old MAC in pHYP */ ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC); if (ret) - goto out_free; + goto out_upregs; port->mac_addr = cb0->port_mac_addr << 16; /* Register new MAC in pHYP */ ret = ehea_broadcast_reg_helper(port, H_REG_BCMC); if (ret) - goto out_free; + goto out_upregs; ret = 0; + +out_upregs: + ehea_update_bcmc_registrations(); + up(&ehea_bcmc_regs.lock); out_free: kfree(cb0); out: @@ -1775,9 +1938,11 @@ static void ehea_set_multicast_list(struct net_device *dev) } ehea_promiscuous(dev, 0); + down(&ehea_bcmc_regs.lock); + if (dev->flags & IFF_ALLMULTI) { ehea_allmulti(dev, 1); - return; + goto out; } ehea_allmulti(dev, 0); @@ -1803,6 +1968,8 @@ static void ehea_set_multicast_list(struct net_device *dev) } out: + ehea_update_bcmc_registrations(); + up(&ehea_bcmc_regs.lock); return; } @@ -2285,6 +2452,8 @@ static int ehea_up(struct net_device *dev) if (port->state == EHEA_PORT_UP) return 0; + down(&ehea_fw_handles.lock); + ret = ehea_port_res_setup(port, port->num_def_qps, port->num_add_tx_qps); if (ret) { @@ -2321,8 +2490,17 @@ static int ehea_up(struct net_device *dev) } } - ret = 0; + down(&ehea_bcmc_regs.lock); + + ret = ehea_broadcast_reg_helper(port, H_REG_BCMC); + if (ret) { + ret = -EIO; + goto out_free_irqs; + } + port->state = EHEA_PORT_UP; + + ret = 0; goto out; out_free_irqs: @@ -2334,6 +2512,12 @@ out: if (ret) ehea_info("Failed starting %s. ret=%i", dev->name, ret); + ehea_update_bcmc_registrations(); + up(&ehea_bcmc_regs.lock); + + ehea_update_firmware_handles(); + up(&ehea_fw_handles.lock); + return ret; } @@ -2382,16 +2566,27 @@ static int ehea_down(struct net_device *dev) if (port->state == EHEA_PORT_DOWN) return 0; + down(&ehea_bcmc_regs.lock); ehea_drop_multicast_list(dev); + ehea_broadcast_reg_helper(port, H_DEREG_BCMC); + ehea_free_interrupts(dev); + down(&ehea_fw_handles.lock); + port->state = EHEA_PORT_DOWN; + ehea_update_bcmc_registrations(); + up(&ehea_bcmc_regs.lock); + ret = ehea_clean_all_portres(port); if (ret) ehea_info("Failed freeing resources for %s. ret=%i", dev->name, ret); + ehea_update_firmware_handles(); + up(&ehea_fw_handles.lock); + return ret; } @@ -2920,19 +3115,12 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT; INIT_WORK(&port->reset_task, ehea_reset_port); - - ret = ehea_broadcast_reg_helper(port, H_REG_BCMC); - if (ret) { - ret = -EIO; - goto out_unreg_port; - } - ehea_set_ethtool_ops(dev); ret = register_netdev(dev); if (ret) { ehea_error("register_netdev failed. ret=%d", ret); - goto out_dereg_bc; + goto out_unreg_port; } port->lro_max_aggr = lro_max_aggr; @@ -2949,9 +3137,6 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, return port; -out_dereg_bc: - ehea_broadcast_reg_helper(port, H_DEREG_BCMC); - out_unreg_port: ehea_unregister_port(port); @@ -2971,7 +3156,6 @@ static void ehea_shutdown_single_port(struct ehea_port *port) { unregister_netdev(port->netdev); ehea_unregister_port(port); - ehea_broadcast_reg_helper(port, H_DEREG_BCMC); kfree(port->mc_list); free_netdev(port->netdev); port->adapter->active_ports--; @@ -3014,7 +3198,6 @@ static int ehea_setup_ports(struct ehea_adapter *adapter) i++; }; - return 0; } @@ -3159,6 +3342,7 @@ static int __devinit ehea_probe_adapter(struct of_device *dev, ehea_error("Invalid ibmebus device probed"); return -EINVAL; } + down(&ehea_fw_handles.lock); adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { @@ -3239,7 +3423,10 @@ out_kill_eq: out_free_ad: kfree(adapter); + out: + ehea_update_firmware_handles(); + up(&ehea_fw_handles.lock); return ret; } @@ -3258,18 +3445,41 @@ static int __devexit ehea_remove(struct of_device *dev) flush_scheduled_work(); + down(&ehea_fw_handles.lock); + ibmebus_free_irq(adapter->neq->attr.ist1, adapter); tasklet_kill(&adapter->neq_tasklet); ehea_destroy_eq(adapter->neq); ehea_remove_adapter_mr(adapter); list_del(&adapter->list); - kfree(adapter); + ehea_update_firmware_handles(); + up(&ehea_fw_handles.lock); + return 0; } +void ehea_crash_handler(void) +{ + int i; + + if (ehea_fw_handles.arr) + for (i = 0; i < ehea_fw_handles.num_entries; i++) + ehea_h_free_resource(ehea_fw_handles.arr[i].adh, + ehea_fw_handles.arr[i].fwh, + FORCE_FREE); + + if (ehea_bcmc_regs.arr) + for (i = 0; i < ehea_bcmc_regs.num_entries; i++) + ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh, + ehea_bcmc_regs.arr[i].port_id, + ehea_bcmc_regs.arr[i].reg_type, + ehea_bcmc_regs.arr[i].macaddr, + 0, H_DEREG_BCMC); +} + static int ehea_reboot_notifier(struct notifier_block *nb, unsigned long action, void *unused) { @@ -3330,7 +3540,12 @@ int __init ehea_module_init(void) INIT_WORK(&ehea_rereg_mr_task, ehea_rereg_mrs); + memset(&ehea_fw_handles, 0, sizeof(ehea_fw_handles)); + memset(&ehea_bcmc_regs, 0, sizeof(ehea_bcmc_regs)); + sema_init(&dlpar_mem_lock, 1); + sema_init(&ehea_fw_handles.lock, 1); + sema_init(&ehea_bcmc_regs.lock, 1); ret = check_module_parm(); if (ret) @@ -3340,12 +3555,18 @@ int __init ehea_module_init(void) if (ret) goto out; - register_reboot_notifier(&ehea_reboot_nb); + ret = register_reboot_notifier(&ehea_reboot_nb); + if (ret) + ehea_info("failed registering reboot notifier"); + + ret = crash_shutdown_register(&ehea_crash_handler); + if (ret) + ehea_info("failed registering crash handler"); ret = ibmebus_register_driver(&ehea_driver); if (ret) { ehea_error("failed registering eHEA device driver on ebus"); - goto out; + goto out2; } ret = driver_create_file(&ehea_driver.driver, @@ -3353,21 +3574,33 @@ int __init ehea_module_init(void) if (ret) { ehea_error("failed to register capabilities attribute, ret=%d", ret); - unregister_reboot_notifier(&ehea_reboot_nb); - ibmebus_unregister_driver(&ehea_driver); - goto out; + goto out3; } + return ret; + +out3: + ibmebus_unregister_driver(&ehea_driver); +out2: + unregister_reboot_notifier(&ehea_reboot_nb); + crash_shutdown_unregister(&ehea_crash_handler); out: return ret; } static void __exit ehea_module_exit(void) { + int ret; + flush_scheduled_work(); driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities); ibmebus_unregister_driver(&ehea_driver); unregister_reboot_notifier(&ehea_reboot_nb); + ret = crash_shutdown_unregister(&ehea_crash_handler); + if (ret) + ehea_info("failed unregistering crash handler"); + kfree(ehea_fw_handles.arr); + kfree(ehea_bcmc_regs.arr); ehea_destroy_busmap(); } |