Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
[pandora-kernel.git] / drivers / infiniband / core / mad.c
index 6edfecf..6b8faca 100644 (file)
@@ -675,10 +675,16 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
        struct ib_mad_port_private *port_priv;
        struct ib_mad_agent_private *recv_mad_agent = NULL;
        struct ib_device *device = mad_agent_priv->agent.device;
-       u8 port_num = mad_agent_priv->agent.port_num;
+       u8 port_num;
        struct ib_wc mad_wc;
        struct ib_send_wr *send_wr = &mad_send_wr->send_wr;
 
+       if (device->node_type == RDMA_NODE_IB_SWITCH &&
+           smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               port_num = send_wr->wr.ud.port_num;
+       else
+               port_num = mad_agent_priv->agent.port_num;
+
        /*
         * Directed route handling starts if the initial LID routed part of
         * a request or the ending LID routed part of a response is empty.
@@ -1839,6 +1845,7 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
        struct ib_mad_private *recv, *response;
        struct ib_mad_list_head *mad_list;
        struct ib_mad_agent_private *mad_agent;
+       int port_num;
 
        response = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
        if (!response)
@@ -1872,25 +1879,50 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
        if (!validate_mad(&recv->mad.mad, qp_info->qp->qp_num))
                goto out;
 
+       if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH)
+               port_num = wc->port_num;
+       else
+               port_num = port_priv->port_num;
+
        if (recv->mad.mad.mad_hdr.mgmt_class ==
            IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+               enum smi_forward_action retsmi;
+
                if (smi_handle_dr_smp_recv(&recv->mad.smp,
                                           port_priv->device->node_type,
-                                          port_priv->port_num,
+                                          port_num,
                                           port_priv->device->phys_port_cnt) ==
                                           IB_SMI_DISCARD)
                        goto out;
 
-               if (smi_check_forward_dr_smp(&recv->mad.smp) == IB_SMI_LOCAL)
+               retsmi = smi_check_forward_dr_smp(&recv->mad.smp);
+               if (retsmi == IB_SMI_LOCAL)
                        goto local;
 
-               if (smi_handle_dr_smp_send(&recv->mad.smp,
-                                          port_priv->device->node_type,
-                                          port_priv->port_num) == IB_SMI_DISCARD)
-                       goto out;
+               if (retsmi == IB_SMI_SEND) { /* don't forward */
+                       if (smi_handle_dr_smp_send(&recv->mad.smp,
+                                                  port_priv->device->node_type,
+                                                  port_num) == IB_SMI_DISCARD)
+                               goto out;
+
+                       if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
+                               goto out;
+               } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
+                       /* forward case for switches */
+                       memcpy(response, recv, sizeof(*response));
+                       response->header.recv_wc.wc = &response->header.wc;
+                       response->header.recv_wc.recv_buf.mad = &response->mad.mad;
+                       response->header.recv_wc.recv_buf.grh = &response->grh;
+
+                       if (!agent_send_response(&response->mad.mad,
+                                                &response->grh, wc,
+                                                port_priv->device,
+                                                smi_get_fwd_port(&recv->mad.smp),
+                                                qp_info->qp->qp_num))
+                               response = NULL;
 
-               if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
                        goto out;
+               }
        }
 
 local:
@@ -1919,7 +1951,7 @@ local:
                                agent_send_response(&response->mad.mad,
                                                    &recv->grh, wc,
                                                    port_priv->device,
-                                                   port_priv->port_num,
+                                                   port_num,
                                                    qp_info->qp->qp_num);
                                goto out;
                        }
@@ -2771,7 +2803,7 @@ static int ib_mad_port_open(struct ib_device *device,
        cq_size = (IB_MAD_QP_SEND_SIZE + IB_MAD_QP_RECV_SIZE) * 2;
        port_priv->cq = ib_create_cq(port_priv->device,
                                     ib_mad_thread_completion_handler,
-                                    NULL, port_priv, cq_size);
+                                    NULL, port_priv, cq_size, 0);
        if (IS_ERR(port_priv->cq)) {
                printk(KERN_ERR PFX "Couldn't create ib_mad CQ\n");
                ret = PTR_ERR(port_priv->cq);