Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / infiniband / core / mad.c
index 5ad41a6..1c3cfbb 100644 (file)
@@ -34,6 +34,7 @@
  * $Id: mad.c 5596 2006-03-03 01:00:07Z sean.hefty $
  */
 #include <linux/dma-mapping.h>
+#include <rdma/ib_cache.h>
 
 #include "mad_priv.h"
 #include "mad_rmpp.h"
@@ -45,8 +46,7 @@ MODULE_DESCRIPTION("kernel IB MAD API");
 MODULE_AUTHOR("Hal Rosenstock");
 MODULE_AUTHOR("Sean Hefty");
 
-
-kmem_cache_t *ib_mad_cache;
+static kmem_cache_t *ib_mad_cache;
 
 static struct list_head ib_mad_port_list;
 static u32 ib_mad_client_id = 0;
@@ -167,6 +167,15 @@ static int is_vendor_method_in_use(
        return 0;
 }
 
+int ib_response_mad(struct ib_mad *mad)
+{
+       return ((mad->mad_hdr.method & IB_MGMT_METHOD_RESP) ||
+               (mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) ||
+               ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_BM) &&
+                (mad->mad_hdr.attr_mod & IB_BM_ATTR_MOD_RESP)));
+}
+EXPORT_SYMBOL(ib_response_mad);
+
 /*
  * ib_register_mad_agent - Register to send/receive MADs
  */
@@ -570,13 +579,6 @@ int ib_unregister_mad_agent(struct ib_mad_agent *mad_agent)
 }
 EXPORT_SYMBOL(ib_unregister_mad_agent);
 
-static inline int response_mad(struct ib_mad *mad)
-{
-       /* Trap represses are responses although response bit is reset */
-       return ((mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) ||
-               (mad->mad_hdr.method & IB_MGMT_METHOD_RESP));
-}
-
 static void dequeue_mad(struct ib_mad_list_head *mad_list)
 {
        struct ib_mad_queue *mad_queue;
@@ -723,7 +725,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
        switch (ret)
        {
        case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY:
-               if (response_mad(&mad_priv->mad.mad) &&
+               if (ib_response_mad(&mad_priv->mad.mad) &&
                    mad_agent_priv->agent.recv_handler) {
                        local->mad_priv = mad_priv;
                        local->recv_mad_agent = mad_agent_priv;
@@ -1551,7 +1553,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
        unsigned long flags;
 
        spin_lock_irqsave(&port_priv->reg_lock, flags);
-       if (response_mad(mad)) {
+       if (ib_response_mad(mad)) {
                u32 hi_tid;
                struct ib_mad_agent_private *entry;
 
@@ -1673,20 +1675,21 @@ static inline int rcv_has_same_class(struct ib_mad_send_wr_private *wr,
                rwc->recv_buf.mad->mad_hdr.mgmt_class;
 }
 
-static inline int rcv_has_same_gid(struct ib_mad_send_wr_private *wr,
+static inline int rcv_has_same_gid(struct ib_mad_agent_private *mad_agent_priv,
+                                  struct ib_mad_send_wr_private *wr,
                                   struct ib_mad_recv_wc *rwc )
 {
        struct ib_ah_attr attr;
        u8 send_resp, rcv_resp;
+       union ib_gid sgid;
+       struct ib_device *device = mad_agent_priv->agent.device;
+       u8 port_num = mad_agent_priv->agent.port_num;
+       u8 lmc;
 
        send_resp = ((struct ib_mad *)(wr->send_buf.mad))->
                     mad_hdr.method & IB_MGMT_METHOD_RESP;
        rcv_resp = rwc->recv_buf.mad->mad_hdr.method & IB_MGMT_METHOD_RESP;
 
-       if (!send_resp && rcv_resp)
-               /* is request/response. GID/LIDs are both local (same). */
-               return 1;
-
        if (send_resp == rcv_resp)
                /* both requests, or both responses. GIDs different */
                return 0;
@@ -1695,48 +1698,78 @@ static inline int rcv_has_same_gid(struct ib_mad_send_wr_private *wr,
                /* Assume not equal, to avoid false positives. */
                return 0;
 
-       if (!(attr.ah_flags & IB_AH_GRH) && !(rwc->wc->wc_flags & IB_WC_GRH))
-               return attr.dlid == rwc->wc->slid;
-       else if ((attr.ah_flags & IB_AH_GRH) &&
-                (rwc->wc->wc_flags & IB_WC_GRH))
-               return memcmp(attr.grh.dgid.raw,
-                             rwc->recv_buf.grh->sgid.raw, 16) == 0;
-       else
+       if (!!(attr.ah_flags & IB_AH_GRH) !=
+           !!(rwc->wc->wc_flags & IB_WC_GRH))
                /* one has GID, other does not.  Assume different */
                return 0;
+
+       if (!send_resp && rcv_resp) {
+               /* is request/response. */
+               if (!(attr.ah_flags & IB_AH_GRH)) {
+                       if (ib_get_cached_lmc(device, port_num, &lmc))
+                               return 0;
+                       return (!lmc || !((attr.src_path_bits ^
+                                          rwc->wc->dlid_path_bits) &
+                                         ((1 << lmc) - 1)));
+               } else {
+                       if (ib_get_cached_gid(device, port_num,
+                                             attr.grh.sgid_index, &sgid))
+                               return 0;
+                       return !memcmp(sgid.raw, rwc->recv_buf.grh->dgid.raw,
+                                      16);
+               }
+       }
+
+       if (!(attr.ah_flags & IB_AH_GRH))
+               return attr.dlid == rwc->wc->slid;
+       else
+               return !memcmp(attr.grh.dgid.raw, rwc->recv_buf.grh->sgid.raw,
+                              16);
+}
+
+static inline int is_direct(u8 class)
+{
+       return (class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE);
 }
+
 struct ib_mad_send_wr_private*
 ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
-                struct ib_mad_recv_wc *mad_recv_wc)
+                struct ib_mad_recv_wc *wc)
 {
-       struct ib_mad_send_wr_private *mad_send_wr;
+       struct ib_mad_send_wr_private *wr;
        struct ib_mad *mad;
 
-       mad = (struct ib_mad *)mad_recv_wc->recv_buf.mad;
+       mad = (struct ib_mad *)wc->recv_buf.mad;
 
-       list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list,
-                           agent_list) {
-               if ((mad_send_wr->tid == mad->mad_hdr.tid) &&
-                   rcv_has_same_class(mad_send_wr, mad_recv_wc) &&
-                   rcv_has_same_gid(mad_send_wr, mad_recv_wc))
-                       return mad_send_wr;
+       list_for_each_entry(wr, &mad_agent_priv->wait_list, agent_list) {
+               if ((wr->tid == mad->mad_hdr.tid) &&
+                   rcv_has_same_class(wr, wc) &&
+                   /*
+                    * Don't check GID for direct routed MADs.
+                    * These might have permissive LIDs.
+                    */
+                   (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+                    rcv_has_same_gid(mad_agent_priv, wr, wc)))
+                       return wr;
        }
 
        /*
         * It's possible to receive the response before we've
         * been notified that the send has completed
         */
-       list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
-                           agent_list) {
-               if (is_data_mad(mad_agent_priv, mad_send_wr->send_buf.mad) &&
-                   mad_send_wr->tid == mad->mad_hdr.tid &&
-                   mad_send_wr->timeout &&
-                   rcv_has_same_class(mad_send_wr, mad_recv_wc) &&
-                   rcv_has_same_gid(mad_send_wr, mad_recv_wc)) {
+       list_for_each_entry(wr, &mad_agent_priv->send_list, agent_list) {
+               if (is_data_mad(mad_agent_priv, wr->send_buf.mad) &&
+                   wr->tid == mad->mad_hdr.tid &&
+                   wr->timeout &&
+                   rcv_has_same_class(wr, wc) &&
+                   /*
+                    * Don't check GID for direct routed MADs.
+                    * These might have permissive LIDs.
+                    */
+                   (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+                    rcv_has_same_gid(mad_agent_priv, wr, wc)))
                        /* Verify request has not been canceled */
-                       return (mad_send_wr->status == IB_WC_SUCCESS) ?
-                               mad_send_wr : NULL;
-               }
+                       return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
        }
        return NULL;
 }
@@ -1744,11 +1777,9 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
 void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr)
 {
        mad_send_wr->timeout = 0;
-       if (mad_send_wr->refcount == 1) {
-               list_del(&mad_send_wr->agent_list);
-               list_add_tail(&mad_send_wr->agent_list,
+       if (mad_send_wr->refcount == 1)
+               list_move_tail(&mad_send_wr->agent_list,
                              &mad_send_wr->mad_agent_priv->done_list);
-       }
 }
 
 static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
@@ -1770,7 +1801,7 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
        }
 
        /* Complete corresponding request */
-       if (response_mad(mad_recv_wc->recv_buf.mad)) {
+       if (ib_response_mad(mad_recv_wc->recv_buf.mad)) {
                spin_lock_irqsave(&mad_agent_priv->lock, flags);
                mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
                if (!mad_send_wr) {
@@ -2067,8 +2098,7 @@ retry:
                queued_send_wr = container_of(mad_list,
                                        struct ib_mad_send_wr_private,
                                        mad_list);
-               list_del(&mad_list->list);
-               list_add_tail(&mad_list->list, &send_queue->list);
+               list_move_tail(&mad_list->list, &send_queue->list);
        }
        spin_unlock_irqrestore(&send_queue->lock, flags);