ehea: DLPAR memory add fix
authorJan-Bernd Themann <ossthema@de.ibm.com>
Mon, 1 Oct 2007 14:33:18 +0000 (16:33 +0200)
committerDavid S. Miller <davem@sunset.davemloft.net>
Wed, 10 Oct 2007 23:54:05 +0000 (16:54 -0700)
Due to stability issues in high load situations the HW queue handling
has to be changed. The HW queues are now stopped and restarted again instead
of destroying and allocating new HW queues.

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/net/ehea/ehea.h
drivers/net/ehea/ehea_main.c
drivers/net/ehea/ehea_phyp.h
drivers/net/ehea/ehea_qmr.c
drivers/net/ehea/ehea_qmr.h

index c0cbd949e33619d3d354af60efa6db0b4dc6a275..30220894b01fa730831f91d02475f04c1b34fd4a 100644 (file)
 #include <asm/io.h>
 
 #define DRV_NAME       "ehea"
-#define DRV_VERSION    "EHEA_0074"
+#define DRV_VERSION    "EHEA_0077"
 
 /* eHEA capability flags */
 #define DLPAR_PORT_ADD_REM 1
 #define DLPAR_MEM_ADD      2
 #define DLPAR_MEM_REM      4
-#define EHEA_CAPABILITIES  (DLPAR_PORT_ADD_REM)
+#define EHEA_CAPABILITIES  (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD)
 
 #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \
        | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
index 62d6c1e5f9d34c3f9f27e4bebce035a23f0b73a8..5bc0a1530eb724a72d55467f9dc8523932d11a86 100644 (file)
@@ -97,6 +97,7 @@ u64 ehea_driver_flags = 0;
 struct workqueue_struct *ehea_driver_wq;
 struct work_struct ehea_rereg_mr_task;
 
+struct semaphore dlpar_mem_lock;
 
 static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev,
                                        const struct of_device_id *id);
@@ -177,16 +178,24 @@ static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
        struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
        struct net_device *dev = pr->port->netdev;
        int max_index_mask = pr->rq1_skba.len - 1;
+       int fill_wqes = pr->rq1_skba.os_skbs + nr_of_wqes;
+       int adder = 0;
        int i;
 
-       if (!nr_of_wqes)
+       pr->rq1_skba.os_skbs = 0;
+
+       if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
+               pr->rq1_skba.index = index;
+               pr->rq1_skba.os_skbs = fill_wqes;
                return;
+       }
 
-       for (i = 0; i < nr_of_wqes; i++) {
+       for (i = 0; i < fill_wqes; i++) {
                if (!skb_arr_rq1[index]) {
                        skb_arr_rq1[index] = netdev_alloc_skb(dev,
                                                              EHEA_L_PKT_SIZE);
                        if (!skb_arr_rq1[index]) {
+                               pr->rq1_skba.os_skbs = fill_wqes - i;
                                ehea_error("%s: no mem for skb/%d wqes filled",
                                           dev->name, i);
                                break;
@@ -194,9 +203,14 @@ static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
                }
                index--;
                index &= max_index_mask;
+               adder++;
        }
+
+       if (adder == 0)
+               return;
+
        /* Ring doorbell */
-       ehea_update_rq1a(pr->qp, i);
+       ehea_update_rq1a(pr->qp, adder);
 }
 
 static int ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
@@ -230,16 +244,21 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr,
        struct sk_buff **skb_arr = q_skba->arr;
        struct ehea_rwqe *rwqe;
        int i, index, max_index_mask, fill_wqes;
+       int adder = 0;
        int ret = 0;
 
        fill_wqes = q_skba->os_skbs + num_wqes;
+       q_skba->os_skbs = 0;
 
-       if (!fill_wqes)
+       if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
+               q_skba->os_skbs = fill_wqes;
                return ret;
+       }
 
        index = q_skba->index;
        max_index_mask = q_skba->len - 1;
        for (i = 0; i < fill_wqes; i++) {
+               u64 tmp_addr;
                struct sk_buff *skb = netdev_alloc_skb(dev, packet_size);
                if (!skb) {
                        ehea_error("%s: no mem for skb/%d wqes filled",
@@ -251,30 +270,37 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr,
                skb_reserve(skb, NET_IP_ALIGN);
 
                skb_arr[index] = skb;
+               tmp_addr = ehea_map_vaddr(skb->data);
+               if (tmp_addr == -1) {
+                       dev_kfree_skb(skb);
+                       q_skba->os_skbs = fill_wqes - i;
+                       ret = 0;
+                       break;
+               }
 
                rwqe = ehea_get_next_rwqe(qp, rq_nr);
                rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
                            | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
                rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
-               rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
+               rwqe->sg_list[0].vaddr = tmp_addr;
                rwqe->sg_list[0].len = packet_size;
                rwqe->data_segments = 1;
 
                index++;
                index &= max_index_mask;
-
-               if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags)))
-                       goto out;
+               adder++;
        }
 
        q_skba->index = index;
+       if (adder == 0)
+               goto out;
 
        /* Ring doorbell */
        iosync();
        if (rq_nr == 2)
-               ehea_update_rq2a(pr->qp, i);
+               ehea_update_rq2a(pr->qp, adder);
        else
-               ehea_update_rq3a(pr->qp, i);
+               ehea_update_rq3a(pr->qp, adder);
 out:
        return ret;
 }
@@ -1967,11 +1993,12 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
                ehea_dump(swqe, 512, "swqe");
        }
 
-       if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags)))
-               goto out;
+       if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
+               netif_stop_queue(dev);
+               swqe->tx_control |= EHEA_SWQE_PURGE;
+       }
 
        ehea_post_swqe(pr->qp, swqe);
-       pr->tx_packets++;
 
        if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
                spin_lock_irqsave(&pr->netif_queue, flags);
@@ -1984,7 +2011,7 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
        }
        dev->trans_start = jiffies;
        spin_unlock(&pr->xmit_lock);
-out:
+
        return NETDEV_TX_OK;
 }
 
@@ -2376,6 +2403,192 @@ static int ehea_stop(struct net_device *dev)
        return ret;
 }
 
+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;
+       struct ehea_swqe *swqe;
+       int wqe_index;
+       int i;
+
+       for (i = 0; i < init_attr->act_nr_send_wqes; i++) {
+               swqe = ehea_get_swqe(&qp, &wqe_index);
+               swqe->tx_control |= EHEA_SWQE_PURGE;
+       }
+}
+
+int ehea_stop_qps(struct net_device *dev)
+{
+       struct ehea_port *port = netdev_priv(dev);
+       struct ehea_adapter *adapter = port->adapter;
+       struct hcp_modify_qp_cb0* cb0;
+       int ret = -EIO;
+       int dret;
+       int i;
+       u64 hret;
+       u64 dummy64 = 0;
+       u16 dummy16 = 0;
+
+       cb0 = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!cb0) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       for (i = 0; i < (port->num_def_qps + port->num_add_tx_qps); i++) {
+               struct ehea_port_res *pr =  &port->port_res[i];
+               struct ehea_qp *qp = pr->qp;
+
+               /* Purge send queue */
+               ehea_purge_sq(qp);
+
+               /* Disable queue pair */
+               hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                           EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
+                                           cb0);
+               if (hret != H_SUCCESS) {
+                       ehea_error("query_ehea_qp failed (1)");
+                       goto out;
+               }
+
+               cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
+               cb0->qp_ctl_reg &= ~H_QP_CR_ENABLED;
+
+               hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                            EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
+                                                           1), cb0, &dummy64,
+                                            &dummy64, &dummy16, &dummy16);
+               if (hret != H_SUCCESS) {
+                       ehea_error("modify_ehea_qp failed (1)");
+                       goto out;
+               }
+
+               hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                           EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
+                                           cb0);
+               if (hret != H_SUCCESS) {
+                       ehea_error("query_ehea_qp failed (2)");
+                       goto out;
+               }
+
+               /* deregister shared memory regions */
+               dret = ehea_rem_smrs(pr);
+               if (dret) {
+                       ehea_error("unreg shared memory region failed");
+                       goto out;
+               }
+       }
+
+       ret = 0;
+out:
+       kfree(cb0);
+
+       return ret;
+}
+
+void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res * pr)
+{
+       struct ehea_qp qp = *orig_qp;
+       struct ehea_qp_init_attr *init_attr = &qp.init_attr;
+       struct ehea_rwqe *rwqe;
+       struct sk_buff **skba_rq2 = pr->rq2_skba.arr;
+       struct sk_buff **skba_rq3 = pr->rq3_skba.arr;
+       struct sk_buff *skb;
+       u32 lkey = pr->recv_mr.lkey;
+
+
+       int i;
+       int index;
+
+       for (i = 0; i < init_attr->act_nr_rwqes_rq2 + 1; i++) {
+               rwqe = ehea_get_next_rwqe(&qp, 2);
+               rwqe->sg_list[0].l_key = lkey;
+               index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
+               skb = skba_rq2[index];
+               if (skb)
+                       rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
+       }
+
+       for (i = 0; i < init_attr->act_nr_rwqes_rq3 + 1; i++) {
+               rwqe = ehea_get_next_rwqe(&qp, 3);
+               rwqe->sg_list[0].l_key = lkey;
+               index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
+               skb = skba_rq3[index];
+               if (skb)
+                       rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
+       }
+}
+
+int ehea_restart_qps(struct net_device *dev)
+{
+       struct ehea_port *port = netdev_priv(dev);
+       struct ehea_adapter *adapter = port->adapter;
+       int ret = 0;
+       int i;
+
+       struct hcp_modify_qp_cb0* cb0;
+       u64 hret;
+       u64 dummy64 = 0;
+       u16 dummy16 = 0;
+
+       cb0 = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!cb0) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       for (i = 0; i < (port->num_def_qps + port->num_add_tx_qps); i++) {
+               struct ehea_port_res *pr =  &port->port_res[i];
+               struct ehea_qp *qp = pr->qp;
+
+               ret = ehea_gen_smrs(pr);
+               if (ret) {
+                       ehea_error("creation of shared memory regions failed");
+                       goto out;
+               }
+
+               ehea_update_rqs(qp, pr);
+
+               /* Enable queue pair */
+               hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                           EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
+                                           cb0);
+               if (hret != H_SUCCESS) {
+                       ehea_error("query_ehea_qp failed (1)");
+                       goto out;
+               }
+
+               cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
+               cb0->qp_ctl_reg |= H_QP_CR_ENABLED;
+
+               hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                            EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
+                                                           1), cb0, &dummy64,
+                                            &dummy64, &dummy16, &dummy16);
+               if (hret != H_SUCCESS) {
+                       ehea_error("modify_ehea_qp failed (1)");
+                       goto out;
+               }
+
+               hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+                                           EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
+                                           cb0);
+               if (hret != H_SUCCESS) {
+                       ehea_error("query_ehea_qp failed (2)");
+                       goto out;
+               }
+
+               /* refill entire queue */
+               ehea_refill_rq1(pr, pr->rq1_skba.index, 0);
+               ehea_refill_rq2(pr, 0);
+               ehea_refill_rq3(pr, 0);
+       }
+out:
+       kfree(cb0);
+
+       return ret;
+}
+
 static void ehea_reset_port(struct work_struct *work)
 {
        int ret;
@@ -2395,6 +2608,8 @@ static void ehea_reset_port(struct work_struct *work)
        if (ret)
                goto out;
 
+       ehea_set_multicast_list(dev);
+
        if (netif_msg_timer(port))
                ehea_info("Device %s resetted successfully", dev->name);
 
@@ -2411,6 +2626,7 @@ static void ehea_rereg_mrs(struct work_struct *work)
        int ret, i;
        struct ehea_adapter *adapter;
 
+       down(&dlpar_mem_lock);
        ehea_info("LPAR memory enlarged - re-initializing driver");
 
        list_for_each_entry(adapter, &adapter_list, list)
@@ -2423,14 +2639,14 @@ static void ehea_rereg_mrs(struct work_struct *work)
                                        struct net_device *dev = port->netdev;
 
                                        if (dev->flags & IFF_UP) {
-                                               ehea_info("stopping %s",
-                                                         dev->name);
                                                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);
-
-                                               ehea_down(dev);
                                                up(&port->port_lock);
                                        }
                                }
@@ -2446,10 +2662,11 @@ static void ehea_rereg_mrs(struct work_struct *work)
                }
 
        ehea_destroy_busmap();
-
        ret = ehea_create_busmap();
-       if (ret)
+       if (ret) {
+               ehea_error("creating ehea busmap failed");
                goto out;
+       }
 
        clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
 
@@ -2471,21 +2688,18 @@ static void ehea_rereg_mrs(struct work_struct *work)
                                        struct net_device *dev = port->netdev;
 
                                        if (dev->flags & IFF_UP) {
-                                               ehea_info("restarting %s",
-                                                         dev->name);
                                                down(&port->port_lock);
-
-                                               ret = ehea_up(dev);
-                                               if (!ret) {
-                                                       port_napi_enable(port);
+                                               port_napi_enable(port);
+                                               ret = ehea_restart_qps(dev);
+                                               if (!ret)
                                                        netif_wake_queue(dev);
-                                               }
-
                                                up(&port->port_lock);
                                        }
                                }
                        }
                }
+       up(&dlpar_mem_lock);
+       ehea_info("re-initializing driver complete");
 out:
        return;
 }
@@ -2494,7 +2708,8 @@ static void ehea_tx_watchdog(struct net_device *dev)
 {
        struct ehea_port *port = netdev_priv(dev);
 
-       if (netif_carrier_ok(dev))
+       if (netif_carrier_ok(dev) &&
+           !test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))
                queue_work(port->adapter->ehea_wq, &port->reset_task);
 }
 
@@ -3139,6 +3354,7 @@ int __init ehea_module_init(void)
        ehea_driver_wq = create_workqueue("ehea_driver_wq");
 
        INIT_WORK(&ehea_rereg_mr_task, ehea_rereg_mrs);
+       sema_init(&dlpar_mem_lock, 1);
 
        ret = check_module_parm();
        if (ret)
index 89b63531ff261261ba0e897ce7938049482dfc4c..faa191d23b8614f095bb0a39bc93673304cb330d 100644 (file)
@@ -126,6 +126,7 @@ struct hcp_modify_qp_cb0 {
 #define H_QP_CR_STATE_RDY2RCV      0x0000030000000000ULL /*  Ready to recv */
 #define H_QP_CR_STATE_RDY2SND      0x0000050000000000ULL /*  Ready to send */
 #define H_QP_CR_STATE_ERROR        0x0000800000000000ULL /*  Error */
+#define H_QP_CR_RES_STATE          0x0000007F00000000ULL /* Resultant state */
 
 struct hcp_modify_qp_cb1 {
        u32 qpn;                /* 00 */
index c82e245960746ed2f126a4f4b78d77ec30eba3bc..329a25248d75c13aa7a6d5ca348675dd6c08bddc 100644 (file)
@@ -563,8 +563,7 @@ int ehea_destroy_qp(struct ehea_qp *qp)
 int ehea_create_busmap( void )
 {
        u64 vaddr = EHEA_BUSMAP_START;
-       unsigned long abs_max_pfn = 0;
-       unsigned long sec_max_pfn;
+       unsigned long high_section_index = 0;
        int i;
 
        /*
@@ -574,14 +573,10 @@ int ehea_create_busmap( void )
        ehea_bmap.valid_sections = 0;
 
        for (i = 0; i < NR_MEM_SECTIONS; i++)
-               if (valid_section_nr(i)) {
-                       sec_max_pfn = section_nr_to_pfn(i);
-                       if (sec_max_pfn > abs_max_pfn)
-                               abs_max_pfn = sec_max_pfn;
-                       ehea_bmap.valid_sections++;
-               }
+               if (valid_section_nr(i))
+                       high_section_index = i;
 
-       ehea_bmap.entries = abs_max_pfn / EHEA_PAGES_PER_SECTION + 1;
+       ehea_bmap.entries = high_section_index + 1;
        ehea_bmap.vaddr = vmalloc(ehea_bmap.entries * sizeof(*ehea_bmap.vaddr));
 
        if (!ehea_bmap.vaddr)
@@ -593,6 +588,7 @@ int ehea_create_busmap( void )
                if (pfn_valid(pfn)) {
                        ehea_bmap.vaddr[i] = vaddr;
                        vaddr += EHEA_SECTSIZE;
+                       ehea_bmap.valid_sections++;
                } else
                        ehea_bmap.vaddr[i] = 0;
        }
@@ -637,7 +633,7 @@ int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
 
        mr_len = ehea_bmap.valid_sections * EHEA_SECTSIZE;
 
-       pt =  kzalloc(EHEA_MAX_RPAGE * sizeof(u64), GFP_KERNEL);
+       pt =  kzalloc(PAGE_SIZE, GFP_KERNEL);
        if (!pt) {
                ehea_error("no mem");
                ret = -ENOMEM;
@@ -660,8 +656,8 @@ int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
                        void *sectbase = __va(i << SECTION_SIZE_BITS);
                        unsigned long k = 0;
 
-                       for (j = 0; j < (PAGES_PER_SECTION / EHEA_MAX_RPAGE);
-                             j++) {
+                       for (j = 0; j < (EHEA_PAGES_PER_SECTION /
+                                        EHEA_MAX_RPAGE); j++) {
 
                                for (m = 0; m < EHEA_MAX_RPAGE; m++) {
                                        pg = sectbase + ((k++) * EHEA_PAGESIZE);
index b71f8452a5e36be88a5c1d4283695f4cfec1a261..562de0ebdd851a3e3e3cabee9f41df66fcaa4e1a 100644 (file)
@@ -39,7 +39,7 @@
 #define EHEA_PAGESHIFT         12
 #define EHEA_PAGESIZE          (1UL << EHEA_PAGESHIFT)
 #define EHEA_SECTSIZE          (1UL << 24)
-#define EHEA_PAGES_PER_SECTION (EHEA_SECTSIZE >> PAGE_SHIFT)
+#define EHEA_PAGES_PER_SECTION (EHEA_SECTSIZE >> EHEA_PAGESHIFT)
 
 #if (1UL << SECTION_SIZE_BITS) < EHEA_SECTSIZE
 #error eHEA module can't work if kernel sectionsize < ehea sectionsize
@@ -145,7 +145,7 @@ struct ehea_rwqe {
 #define EHEA_CQE_VLAN_TAG_XTRACT   0x0400
 
 #define EHEA_CQE_TYPE_RQ           0x60
-#define EHEA_CQE_STAT_ERR_MASK     0x721F
+#define EHEA_CQE_STAT_ERR_MASK     0x720F
 #define EHEA_CQE_STAT_FAT_ERR_MASK 0x1F
 #define EHEA_CQE_STAT_ERR_TCP      0x4000
 #define EHEA_CQE_STAT_ERR_IP       0x2000