#include <linux/if_ether.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/memory.h>
+#include <asm/kexec.h>
+#include <linux/mutex.h>
#include <net/ip.h>
static LIST_HEAD(adapter_list);
u64 ehea_driver_flags;
struct work_struct ehea_rereg_mr_task;
+static DEFINE_MUTEX(dlpar_mem_lock);
+struct ehea_fw_handle_array ehea_fw_handles;
+struct ehea_bcmc_reg_array ehea_bcmc_regs;
-struct semaphore dlpar_mem_lock;
static int __devinit ehea_probe_adapter(struct of_device *dev,
const struct of_device_id *id);
}
}
+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);
pr->rq1_skba.os_skbs = 0;
if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
- pr->rq1_skba.index = index;
+ if (nr_of_wqes > 0)
+ pr->rq1_skba.index = index;
pr->rq1_skba.os_skbs = fill_wqes;
return;
}
init_attr->act_nr_rwqes_rq2,
init_attr->act_nr_rwqes_rq3);
- ret = ehea_init_q_skba(&pr->sq_skba, init_attr->act_nr_send_wqes + 1);
+ pr->sq_skba_size = init_attr->act_nr_send_wqes + 1;
+
+ ret = ehea_init_q_skba(&pr->sq_skba, pr->sq_skba_size);
ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
+ mutex_lock(&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();
+ mutex_unlock(&ehea_bcmc_regs.lock);
out_free:
kfree(cb0);
out:
}
ehea_promiscuous(dev, 0);
+ mutex_lock(&ehea_bcmc_regs.lock);
+
if (dev->flags & IFF_ALLMULTI) {
ehea_allmulti(dev, 1);
- return;
+ goto out;
}
ehea_allmulti(dev, 0);
}
out:
+ ehea_update_bcmc_registrations();
+ mutex_unlock(&ehea_bcmc_regs.lock);
return;
}
if (port->state == EHEA_PORT_UP)
return 0;
+ mutex_lock(&ehea_fw_handles.lock);
+
ret = ehea_port_res_setup(port, port->num_def_qps,
port->num_add_tx_qps);
if (ret) {
}
}
- ret = 0;
+ mutex_lock(&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:
if (ret)
ehea_info("Failed starting %s. ret=%i", dev->name, ret);
+ ehea_update_bcmc_registrations();
+ mutex_unlock(&ehea_bcmc_regs.lock);
+
+ ehea_update_firmware_handles();
+ mutex_unlock(&ehea_fw_handles.lock);
+
return ret;
}
int ret;
struct ehea_port *port = netdev_priv(dev);
- down(&port->port_lock);
+ mutex_lock(&port->port_lock);
if (netif_msg_ifup(port))
ehea_info("enabling port %s", dev->name);
netif_start_queue(dev);
}
- up(&port->port_lock);
+ mutex_unlock(&port->port_lock);
return ret;
}
if (port->state == EHEA_PORT_DOWN)
return 0;
+ mutex_lock(&ehea_fw_handles.lock);
+
+ mutex_lock(&ehea_bcmc_regs.lock);
ehea_drop_multicast_list(dev);
+ ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
+
ehea_free_interrupts(dev);
port->state = EHEA_PORT_DOWN;
+ ehea_update_bcmc_registrations();
+ mutex_unlock(&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();
+ mutex_unlock(&ehea_fw_handles.lock);
+
return ret;
}
ehea_info("disabling port %s", dev->name);
flush_scheduled_work();
- down(&port->port_lock);
+ mutex_lock(&port->port_lock);
netif_stop_queue(dev);
port_napi_disable(port);
ret = ehea_down(dev);
- up(&port->port_lock);
+ mutex_unlock(&port->port_lock);
return ret;
}
-void ehea_purge_sq(struct ehea_qp *orig_qp)
+static void ehea_purge_sq(struct ehea_qp *orig_qp)
{
struct ehea_qp qp = *orig_qp;
struct ehea_qp_init_attr *init_attr = &qp.init_attr;
}
}
+static void ehea_flush_sq(struct ehea_port *port)
+{
+ int i;
+
+ for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+ struct ehea_port_res *pr = &port->port_res[i];
+ int swqe_max = pr->sq_skba_size - 2 - pr->swqe_ll_count;
+ int k = 0;
+ while (atomic_read(&pr->swqe_avail) < swqe_max) {
+ msleep(5);
+ if (++k == 20)
+ break;
+ }
+ }
+}
+
int ehea_stop_qps(struct net_device *dev)
{
struct ehea_port *port = netdev_priv(dev);
struct net_device *dev = port->netdev;
port->resets++;
- down(&port->port_lock);
+ mutex_lock(&port->port_lock);
netif_stop_queue(dev);
port_napi_disable(port);
netif_wake_queue(dev);
out:
- up(&port->port_lock);
+ mutex_unlock(&port->port_lock);
return;
}
int ret, i;
struct ehea_adapter *adapter;
- down(&dlpar_mem_lock);
+ mutex_lock(&dlpar_mem_lock);
ehea_info("LPAR memory enlarged - re-initializing driver");
list_for_each_entry(adapter, &adapter_list, list)
/* Shutdown all ports */
for (i = 0; i < EHEA_MAX_PORTS; i++) {
struct ehea_port *port = adapter->port[i];
+ struct net_device *dev;
- if (port) {
- struct net_device *dev = port->netdev;
+ if (!port)
+ continue;
- if (dev->flags & IFF_UP) {
- down(&port->port_lock);
- netif_stop_queue(dev);
- ret = ehea_stop_qps(dev);
- if (ret) {
- up(&port->port_lock);
- goto out;
- }
- port_napi_disable(port);
- up(&port->port_lock);
+ dev = port->netdev;
+
+ if (dev->flags & IFF_UP) {
+ mutex_lock(&port->port_lock);
+ netif_stop_queue(dev);
+ ehea_flush_sq(port);
+ ret = ehea_stop_qps(dev);
+ if (ret) {
+ mutex_unlock(&port->port_lock);
+ goto out;
}
+ port_napi_disable(port);
+ mutex_unlock(&port->port_lock);
}
}
struct net_device *dev = port->netdev;
if (dev->flags & IFF_UP) {
- down(&port->port_lock);
+ mutex_lock(&port->port_lock);
port_napi_enable(port);
ret = ehea_restart_qps(dev);
if (!ret)
netif_wake_queue(dev);
- up(&port->port_lock);
+ mutex_unlock(&port->port_lock);
}
}
}
}
- up(&dlpar_mem_lock);
+ mutex_unlock(&dlpar_mem_lock);
ehea_info("re-initializing driver complete");
out:
return;
port = netdev_priv(dev);
- sema_init(&port->port_lock, 1);
+ mutex_init(&port->port_lock);
port->state = EHEA_PORT_DOWN;
port->sig_comp_iv = sq_entries / 10;
dev->vlan_rx_add_vid = ehea_vlan_rx_add_vid;
dev->vlan_rx_kill_vid = ehea_vlan_rx_kill_vid;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
- | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX
+ | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_TX
| NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER
| NETIF_F_LLTX;
dev->tx_timeout = &ehea_tx_watchdog;
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;
return port;
-out_dereg_bc:
- ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
-
out_unreg_port:
ehea_unregister_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--;
i++;
};
-
return 0;
}
ehea_error("Invalid ibmebus device probed");
return -EINVAL;
}
+ mutex_lock(&ehea_fw_handles.lock);
adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
if (!adapter) {
out_free_ad:
kfree(adapter);
+
out:
+ ehea_update_firmware_handles();
+ mutex_unlock(&ehea_fw_handles.lock);
return ret;
}
flush_scheduled_work();
+ mutex_lock(&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();
+ mutex_unlock(&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_mem_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ switch (action) {
+ case MEM_OFFLINE:
+ ehea_info("memory has been removed");
+ ehea_rereg_mrs(NULL);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ehea_mem_nb = {
+ .notifier_call = ehea_mem_notifier,
+};
+
static int ehea_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused)
{
INIT_WORK(&ehea_rereg_mr_task, ehea_rereg_mrs);
- sema_init(&dlpar_mem_lock, 1);
+ memset(&ehea_fw_handles, 0, sizeof(ehea_fw_handles));
+ memset(&ehea_bcmc_regs, 0, sizeof(ehea_bcmc_regs));
+
+ mutex_init(&ehea_fw_handles.lock);
+ mutex_init(&ehea_bcmc_regs.lock);
ret = check_module_parm();
if (ret)
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 = register_memory_notifier(&ehea_mem_nb);
+ if (ret)
+ ehea_info("failed registering memory remove 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,
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_memory_notifier(&ehea_mem_nb);
+ 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");
+ unregister_memory_notifier(&ehea_mem_nb);
+ kfree(ehea_fw_handles.arr);
+ kfree(ehea_bcmc_regs.arr);
ehea_destroy_busmap();
}