Merge branch 'bkl-removal' into next
[pandora-kernel.git] / drivers / infiniband / core / mad_rmpp.c
index d4704e0..d0ef7d6 100644 (file)
  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
- *
- * $Id: mad_rmpp.c 1921 2005-03-02 22:58:44Z sean.hefty $
  */
 
-#include <linux/dma-mapping.h>
-
 #include "mad_priv.h"
 #include "mad_rmpp.h"
 
@@ -47,8 +43,8 @@ enum rmpp_state {
 struct mad_rmpp_recv {
        struct ib_mad_agent_private *agent;
        struct list_head list;
-       struct work_struct timeout_work;
-       struct work_struct cleanup_work;
+       struct delayed_work timeout_work;
+       struct delayed_work cleanup_work;
        struct completion comp;
        enum rmpp_state state;
        spinlock_t lock;
@@ -60,6 +56,7 @@ struct mad_rmpp_recv {
        int last_ack;
        int seg_num;
        int newwin;
+       int repwin;
 
        __be64 tid;
        u32 src_qp;
@@ -164,17 +161,43 @@ static struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent,
                                 hdr_len, 0, GFP_KERNEL);
        if (IS_ERR(msg))
                ib_destroy_ah(ah);
-       else
+       else {
                msg->ah = ah;
+               msg->context[0] = ah;
+       }
 
        return msg;
 }
 
-void ib_rmpp_send_handler(struct ib_mad_send_wc *mad_send_wc)
+static void ack_ds_ack(struct ib_mad_agent_private *agent,
+                      struct ib_mad_recv_wc *recv_wc)
 {
-       struct ib_rmpp_mad *rmpp_mad = mad_send_wc->send_buf->mad;
+       struct ib_mad_send_buf *msg;
+       struct ib_rmpp_mad *rmpp_mad;
+       int ret;
 
-       if (rmpp_mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_ACK)
+       msg = alloc_response_msg(&agent->agent, recv_wc);
+       if (IS_ERR(msg))
+               return;
+
+       rmpp_mad = msg->mad;
+       memcpy(rmpp_mad, recv_wc->recv_buf.mad, msg->hdr_len);
+
+       rmpp_mad->mad_hdr.method ^= IB_MGMT_METHOD_RESP;
+       ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE);
+       rmpp_mad->rmpp_hdr.seg_num = 0;
+       rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(1);
+
+       ret = ib_post_send_mad(msg, NULL);
+       if (ret) {
+               ib_destroy_ah(msg->ah);
+               ib_free_send_mad(msg);
+       }
+}
+
+void ib_rmpp_send_handler(struct ib_mad_send_wc *mad_send_wc)
+{
+       if (mad_send_wc->send_buf->context[0] == mad_send_wc->send_buf->ah)
                ib_destroy_ah(mad_send_wc->send_buf->ah);
        ib_free_send_mad(mad_send_wc->send_buf);
 }
@@ -208,9 +231,10 @@ static void nack_recv(struct ib_mad_agent_private *agent,
        }
 }
 
-static void recv_timeout_handler(void *data)
+static void recv_timeout_handler(struct work_struct *work)
 {
-       struct mad_rmpp_recv *rmpp_recv = data;
+       struct mad_rmpp_recv *rmpp_recv =
+               container_of(work, struct mad_rmpp_recv, timeout_work.work);
        struct ib_mad_recv_wc *rmpp_wc;
        unsigned long flags;
 
@@ -229,9 +253,10 @@ static void recv_timeout_handler(void *data)
        ib_free_recv_mad(rmpp_wc);
 }
 
-static void recv_cleanup_handler(void *data)
+static void recv_cleanup_handler(struct work_struct *work)
 {
-       struct mad_rmpp_recv *rmpp_recv = data;
+       struct mad_rmpp_recv *rmpp_recv =
+               container_of(work, struct mad_rmpp_recv, cleanup_work.work);
        unsigned long flags;
 
        spin_lock_irqsave(&rmpp_recv->agent->lock, flags);
@@ -260,8 +285,8 @@ create_rmpp_recv(struct ib_mad_agent_private *agent,
 
        rmpp_recv->agent = agent;
        init_completion(&rmpp_recv->comp);
-       INIT_WORK(&rmpp_recv->timeout_work, recv_timeout_handler, rmpp_recv);
-       INIT_WORK(&rmpp_recv->cleanup_work, recv_cleanup_handler, rmpp_recv);
+       INIT_DELAYED_WORK(&rmpp_recv->timeout_work, recv_timeout_handler);
+       INIT_DELAYED_WORK(&rmpp_recv->cleanup_work, recv_cleanup_handler);
        spin_lock_init(&rmpp_recv->lock);
        rmpp_recv->state = RMPP_STATE_ACTIVE;
        atomic_set(&rmpp_recv->refcount, 1);
@@ -271,6 +296,7 @@ create_rmpp_recv(struct ib_mad_agent_private *agent,
        rmpp_recv->newwin = 1;
        rmpp_recv->seg_num = 1;
        rmpp_recv->last_ack = 0;
+       rmpp_recv->repwin = 1;
 
        mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr;
        rmpp_recv->tid = mad_hdr->tid;
@@ -365,7 +391,7 @@ static inline int window_size(struct ib_mad_agent_private *agent)
 static struct ib_mad_recv_buf * find_seg_location(struct list_head *rmpp_list,
                                                  int seg_num)
 {
-        struct ib_mad_recv_buf *seg_buf;
+       struct ib_mad_recv_buf *seg_buf;
        int cur_seg_num;
 
        list_for_each_entry_reverse(seg_buf, rmpp_list, list) {
@@ -591,6 +617,16 @@ static inline void adjust_last_ack(struct ib_mad_send_wr_private *wr,
                        break;
 }
 
+static void process_ds_ack(struct ib_mad_agent_private *agent,
+                          struct ib_mad_recv_wc *mad_recv_wc, int newwin)
+{
+       struct mad_rmpp_recv *rmpp_recv;
+
+       rmpp_recv = find_rmpp_recv(agent, mad_recv_wc);
+       if (rmpp_recv && rmpp_recv->state == RMPP_STATE_COMPLETE)
+               rmpp_recv->repwin = newwin;
+}
+
 static void process_rmpp_ack(struct ib_mad_agent_private *agent,
                             struct ib_mad_recv_wc *mad_recv_wc)
 {
@@ -616,8 +652,18 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
 
        spin_lock_irqsave(&agent->lock, flags);
        mad_send_wr = ib_find_send_mad(agent, mad_recv_wc);
-       if (!mad_send_wr)
-               goto out;       /* Unmatched ACK */
+       if (!mad_send_wr) {
+               if (!seg_num)
+                       process_ds_ack(agent, mad_recv_wc, newwin);
+               goto out;       /* Unmatched or DS RMPP ACK */
+       }
+
+       if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) &&
+           (mad_send_wr->timeout)) {
+               spin_unlock_irqrestore(&agent->lock, flags);
+               ack_ds_ack(agent, mad_recv_wc);
+               return;         /* Repeated ACK for DS RMPP transaction */
+       }
 
        if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) ||
            (!mad_send_wr->timeout) || (mad_send_wr->status != IB_WC_SUCCESS))
@@ -636,7 +682,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
 
        if (seg_num > mad_send_wr->last_ack) {
                adjust_last_ack(mad_send_wr, seg_num);
-               mad_send_wr->retries = mad_send_wr->send_buf.retries;
+               mad_send_wr->retries_left = mad_send_wr->max_retries;
        }
        mad_send_wr->newwin = newwin;
        if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) {
@@ -656,6 +702,9 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
                if (mad_send_wr->refcount == 1)
                        ib_reset_mad_timeout(mad_send_wr,
                                             mad_send_wr->send_buf.timeout_ms);
+               spin_unlock_irqrestore(&agent->lock, flags);
+               ack_ds_ack(agent, mad_recv_wc);
+               return;
        } else if (mad_send_wr->refcount == 1 &&
                   mad_send_wr->seg_num < mad_send_wr->newwin &&
                   mad_send_wr->seg_num < mad_send_wr->send_buf.seg_count) {
@@ -665,8 +714,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
                        goto out;
 
                mad_send_wr->refcount++;
-               list_del(&mad_send_wr->agent_list);
-               list_add_tail(&mad_send_wr->agent_list,
+               list_move_tail(&mad_send_wr->agent_list,
                              &mad_send_wr->mad_agent_priv->send_list);
        }
 out:
@@ -773,6 +821,39 @@ out:
        return NULL;
 }
 
+static int init_newwin(struct ib_mad_send_wr_private *mad_send_wr)
+{
+       struct ib_mad_agent_private *agent = mad_send_wr->mad_agent_priv;
+       struct ib_mad_hdr *mad_hdr = mad_send_wr->send_buf.mad;
+       struct mad_rmpp_recv *rmpp_recv;
+       struct ib_ah_attr ah_attr;
+       unsigned long flags;
+       int newwin = 1;
+
+       if (!(mad_hdr->method & IB_MGMT_METHOD_RESP))
+               goto out;
+
+       spin_lock_irqsave(&agent->lock, flags);
+       list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) {
+               if (rmpp_recv->tid != mad_hdr->tid ||
+                   rmpp_recv->mgmt_class != mad_hdr->mgmt_class ||
+                   rmpp_recv->class_version != mad_hdr->class_version ||
+                   (rmpp_recv->method & IB_MGMT_METHOD_RESP))
+                       continue;
+
+               if (ib_query_ah(mad_send_wr->send_buf.ah, &ah_attr))
+                       continue;
+
+               if (rmpp_recv->slid == ah_attr.dlid) {
+                       newwin = rmpp_recv->repwin;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&agent->lock, flags);
+out:
+       return newwin;
+}
+
 int ib_send_rmpp_mad(struct ib_mad_send_wr_private *mad_send_wr)
 {
        struct ib_rmpp_mad *rmpp_mad;
@@ -788,7 +869,7 @@ int ib_send_rmpp_mad(struct ib_mad_send_wr_private *mad_send_wr)
                return IB_RMPP_RESULT_INTERNAL;
        }
 
-       mad_send_wr->newwin = 1;
+       mad_send_wr->newwin = init_newwin(mad_send_wr);
 
        /* We need to wait for the final ACK even if there isn't a response */
        mad_send_wr->refcount += (mad_send_wr->timeout == 0);