/*
* Copyright (c) 2005 Intel Inc. All rights reserved.
- * Copyright (c) 2005-2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2005 Voltaire, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
struct list_head list;
struct work_struct timeout_work;
struct work_struct cleanup_work;
- struct completion comp;
+ wait_queue_head_t wait;
enum rmpp_state state;
spinlock_t lock;
atomic_t refcount;
u8 method;
};
-static inline void deref_rmpp_recv(struct mad_rmpp_recv *rmpp_recv)
-{
- if (atomic_dec_and_test(&rmpp_recv->refcount))
- complete(&rmpp_recv->comp);
-}
-
static void destroy_rmpp_recv(struct mad_rmpp_recv *rmpp_recv)
{
- deref_rmpp_recv(rmpp_recv);
- wait_for_completion(&rmpp_recv->comp);
+ atomic_dec(&rmpp_recv->refcount);
+ wait_event(rmpp_recv->wait, !atomic_read(&rmpp_recv->refcount));
ib_destroy_ah(rmpp_recv->ah);
kfree(rmpp_recv);
}
}
}
-static void format_ack(struct ib_mad_send_buf *msg,
+static int data_offset(u8 mgmt_class)
+{
+ if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
+ return IB_MGMT_SA_HDR;
+ else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
+ (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
+ return IB_MGMT_VENDOR_HDR;
+ else
+ return IB_MGMT_RMPP_HDR;
+}
+
+static void format_ack(struct ib_rmpp_mad *ack,
struct ib_rmpp_mad *data,
struct mad_rmpp_recv *rmpp_recv)
{
- struct ib_rmpp_mad *ack = msg->mad;
unsigned long flags;
- memcpy(ack, &data->mad_hdr, msg->hdr_len);
+ memcpy(&ack->mad_hdr, &data->mad_hdr,
+ data_offset(data->mad_hdr.mgmt_class));
ack->mad_hdr.method ^= IB_MGMT_METHOD_RESP;
ack->rmpp_hdr.rmpp_type = IB_MGMT_RMPP_TYPE_ACK;
struct ib_mad_recv_wc *recv_wc)
{
struct ib_mad_send_buf *msg;
- int ret, hdr_len;
+ int ret;
- hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp,
- recv_wc->wc->pkey_index, 1, hdr_len,
- 0, GFP_KERNEL);
+ recv_wc->wc->pkey_index, 1, IB_MGMT_RMPP_HDR,
+ IB_MGMT_RMPP_DATA, GFP_KERNEL);
if (!msg)
return;
- format_ack(msg, (struct ib_rmpp_mad *) recv_wc->recv_buf.mad, rmpp_recv);
+ format_ack(msg->mad, (struct ib_rmpp_mad *) recv_wc->recv_buf.mad,
+ rmpp_recv);
msg->ah = rmpp_recv->ah;
ret = ib_post_send_mad(msg, NULL);
if (ret)
{
struct ib_mad_send_buf *msg;
struct ib_ah *ah;
- int hdr_len;
ah = ib_create_ah_from_wc(agent->qp->pd, recv_wc->wc,
recv_wc->recv_buf.grh, agent->port_num);
if (IS_ERR(ah))
return (void *) ah;
- hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(agent, recv_wc->wc->src_qp,
recv_wc->wc->pkey_index, 1,
- hdr_len, 0, GFP_KERNEL);
+ IB_MGMT_RMPP_HDR, IB_MGMT_RMPP_DATA,
+ GFP_KERNEL);
if (IS_ERR(msg))
ib_destroy_ah(ah);
else
return;
rmpp_mad = msg->mad;
- memcpy(rmpp_mad, recv_wc->recv_buf.mad, msg->hdr_len);
+ memcpy(rmpp_mad, recv_wc->recv_buf.mad,
+ data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class));
rmpp_mad->mad_hdr.method ^= IB_MGMT_METHOD_RESP;
rmpp_mad->rmpp_hdr.rmpp_version = IB_MGMT_RMPP_VERSION;
goto error;
rmpp_recv->agent = agent;
- init_completion(&rmpp_recv->comp);
+ init_waitqueue_head(&rmpp_recv->wait);
INIT_WORK(&rmpp_recv->timeout_work, recv_timeout_handler, rmpp_recv);
INIT_WORK(&rmpp_recv->cleanup_work, recv_cleanup_handler, rmpp_recv);
spin_lock_init(&rmpp_recv->lock);
return NULL;
}
+static inline void deref_rmpp_recv(struct mad_rmpp_recv *rmpp_recv)
+{
+ if (atomic_dec_and_test(&rmpp_recv->refcount))
+ wake_up(&rmpp_recv->wait);
+}
+
static struct mad_rmpp_recv *
find_rmpp_recv(struct ib_mad_agent_private *agent,
struct ib_mad_recv_wc *mad_recv_wc)
rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad;
- hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
+ hdr_size = data_offset(rmpp_mad->mad_hdr.mgmt_class);
data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
if (pad > IB_MGMT_RMPP_DATA || pad < 0)
return rmpp_wc;
}
+void ib_coalesce_recv_mad(struct ib_mad_recv_wc *mad_recv_wc, void *buf)
+{
+ struct ib_mad_recv_buf *seg_buf;
+ struct ib_rmpp_mad *rmpp_mad;
+ void *data;
+ int size, len, offset;
+ u8 flags;
+
+ len = mad_recv_wc->mad_len;
+ if (len <= sizeof(struct ib_mad)) {
+ memcpy(buf, mad_recv_wc->recv_buf.mad, len);
+ return;
+ }
+
+ offset = data_offset(mad_recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
+
+ list_for_each_entry(seg_buf, &mad_recv_wc->rmpp_list, list) {
+ rmpp_mad = (struct ib_rmpp_mad *)seg_buf->mad;
+ flags = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr);
+
+ if (flags & IB_MGMT_RMPP_FLAG_FIRST) {
+ data = rmpp_mad;
+ size = sizeof(*rmpp_mad);
+ } else {
+ data = (void *) rmpp_mad + offset;
+ if (flags & IB_MGMT_RMPP_FLAG_LAST)
+ size = len;
+ else
+ size = sizeof(*rmpp_mad) - offset;
+ }
+
+ memcpy(buf, data, size);
+ len -= size;
+ buf += size;
+ }
+}
+EXPORT_SYMBOL(ib_coalesce_recv_mad);
+
static struct ib_mad_recv_wc *
continue_rmpp(struct ib_mad_agent_private *agent,
struct ib_mad_recv_wc *mad_recv_wc)
return mad_recv_wc;
}
+static inline u64 get_seg_addr(struct ib_mad_send_wr_private *mad_send_wr)
+{
+ return mad_send_wr->sg_list[0].addr + mad_send_wr->data_offset +
+ (sizeof(struct ib_rmpp_mad) - mad_send_wr->data_offset) *
+ (mad_send_wr->seg_num - 1);
+}
+
static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr)
{
struct ib_rmpp_mad *rmpp_mad;
int timeout;
- u32 paylen = 0;
+ u32 paylen;
rmpp_mad = mad_send_wr->send_buf.mad;
ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE);
- rmpp_mad->rmpp_hdr.seg_num = cpu_to_be32(++mad_send_wr->seg_num);
+ rmpp_mad->rmpp_hdr.seg_num = cpu_to_be32(mad_send_wr->seg_num);
if (mad_send_wr->seg_num == 1) {
rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_FIRST;
- paylen = mad_send_wr->send_buf.seg_count * IB_MGMT_RMPP_DATA -
+ paylen = mad_send_wr->total_seg * IB_MGMT_RMPP_DATA -
mad_send_wr->pad;
+ rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen);
+ mad_send_wr->sg_list[0].length = sizeof(struct ib_rmpp_mad);
+ } else {
+ mad_send_wr->send_wr.num_sge = 2;
+ mad_send_wr->sg_list[0].length = mad_send_wr->data_offset;
+ mad_send_wr->sg_list[1].addr = get_seg_addr(mad_send_wr);
+ mad_send_wr->sg_list[1].length = sizeof(struct ib_rmpp_mad) -
+ mad_send_wr->data_offset;
+ mad_send_wr->sg_list[1].lkey = mad_send_wr->sg_list[0].lkey;
+ rmpp_mad->rmpp_hdr.paylen_newwin = 0;
}
- if (mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count) {
+ if (mad_send_wr->seg_num == mad_send_wr->total_seg) {
rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_LAST;
paylen = IB_MGMT_RMPP_DATA - mad_send_wr->pad;
+ rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen);
}
- rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen);
/* 2 seconds for an ACK until we can find the packet lifetime */
timeout = mad_send_wr->send_buf.timeout_ms;
if (!timeout || timeout > 2000)
mad_send_wr->timeout = msecs_to_jiffies(2000);
-
+ mad_send_wr->seg_num++;
return ib_send_mad(mad_send_wr);
}
-static void abort_send(struct ib_mad_agent_private *agent,
- struct ib_mad_recv_wc *mad_recv_wc, u8 rmpp_status)
+static void abort_send(struct ib_mad_agent_private *agent, __be64 tid,
+ u8 rmpp_status)
{
struct ib_mad_send_wr_private *mad_send_wr;
struct ib_mad_send_wc wc;
unsigned long flags;
spin_lock_irqsave(&agent->lock, flags);
- mad_send_wr = ib_find_send_mad(agent, mad_recv_wc);
+ mad_send_wr = ib_find_send_mad(agent, tid);
if (!mad_send_wr)
goto out; /* Unmatched send */
- if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) ||
+ if ((mad_send_wr->last_ack == mad_send_wr->total_seg) ||
(!mad_send_wr->timeout) || (mad_send_wr->status != IB_WC_SUCCESS))
goto out; /* Send is already done */
spin_unlock_irqrestore(&agent->lock, flags);
}
-static inline void adjust_last_ack(struct ib_mad_send_wr_private *wr,
- int seg_num)
-{
- struct list_head *list;
-
- wr->last_ack = seg_num;
- list = &wr->last_ack_seg->list;
- list_for_each_entry(wr->last_ack_seg, list, list)
- if (wr->last_ack_seg->num == seg_num)
- break;
-}
-
static void process_rmpp_ack(struct ib_mad_agent_private *agent,
struct ib_mad_recv_wc *mad_recv_wc)
{
rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
if (rmpp_mad->rmpp_hdr.rmpp_status) {
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
return;
}
seg_num = be32_to_cpu(rmpp_mad->rmpp_hdr.seg_num);
newwin = be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
if (newwin < seg_num) {
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_W2S);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S);
return;
}
spin_lock_irqsave(&agent->lock, flags);
- mad_send_wr = ib_find_send_mad(agent, mad_recv_wc);
+ mad_send_wr = ib_find_send_mad(agent, rmpp_mad->mad_hdr.tid);
if (!mad_send_wr)
goto out; /* Unmatched ACK */
- if ((mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) ||
+ if ((mad_send_wr->last_ack == mad_send_wr->total_seg) ||
(!mad_send_wr->timeout) || (mad_send_wr->status != IB_WC_SUCCESS))
goto out; /* Send is already done */
- if (seg_num > mad_send_wr->send_buf.seg_count ||
- seg_num > mad_send_wr->newwin) {
+ if (seg_num > mad_send_wr->total_seg || seg_num > mad_send_wr->newwin) {
spin_unlock_irqrestore(&agent->lock, flags);
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_S2B);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B);
return;
}
goto out; /* Old ACK */
if (seg_num > mad_send_wr->last_ack) {
- adjust_last_ack(mad_send_wr, seg_num);
+ mad_send_wr->last_ack = seg_num;
mad_send_wr->retries = mad_send_wr->send_buf.retries;
}
mad_send_wr->newwin = newwin;
- if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) {
+ if (mad_send_wr->last_ack == mad_send_wr->total_seg) {
/* If no response is expected, the ACK completes the send */
if (!mad_send_wr->send_buf.timeout_ms) {
struct ib_mad_send_wc wc;
mad_send_wr->send_buf.timeout_ms);
} 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) {
+ mad_send_wr->seg_num <= mad_send_wr->total_seg) {
/* Send failure will just result in a timeout/retry */
ret = send_next_seg(mad_send_wr);
if (ret)
goto out;
mad_send_wr->refcount++;
- list_move_tail(&mad_send_wr->agent_list,
+ list_del(&mad_send_wr->agent_list);
+ list_add_tail(&mad_send_wr->agent_list,
&mad_send_wr->mad_agent_priv->send_list);
}
out:
rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
if (rmpp_mad->rmpp_hdr.rmpp_status != IB_MGMT_RMPP_STATUS_RESX) {
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
} else
- abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ rmpp_mad->rmpp_hdr.rmpp_status);
}
static void process_rmpp_abort(struct ib_mad_agent_private *agent,
if (rmpp_mad->rmpp_hdr.rmpp_status < IB_MGMT_RMPP_STATUS_ABORT_MIN ||
rmpp_mad->rmpp_hdr.rmpp_status > IB_MGMT_RMPP_STATUS_ABORT_MAX) {
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
} else
- abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ rmpp_mad->rmpp_hdr.rmpp_status);
}
struct ib_mad_recv_wc *
return mad_recv_wc;
if (rmpp_mad->rmpp_hdr.rmpp_version != IB_MGMT_RMPP_VERSION) {
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_UNV);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV);
goto out;
}
process_rmpp_abort(agent, mad_recv_wc);
break;
default:
- abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT);
+ abort_send(agent, rmpp_mad->mad_hdr.tid,
+ IB_MGMT_RMPP_STATUS_BADT);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT);
break;
}
int ib_send_rmpp_mad(struct ib_mad_send_wr_private *mad_send_wr)
{
struct ib_rmpp_mad *rmpp_mad;
- int ret;
+ int i, total_len, ret;
rmpp_mad = mad_send_wr->send_buf.mad;
if (!(ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
IB_MGMT_RMPP_FLAG_ACTIVE))
return IB_RMPP_RESULT_UNHANDLED;
- if (rmpp_mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA) {
- mad_send_wr->seg_num = 1;
+ if (rmpp_mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA)
return IB_RMPP_RESULT_INTERNAL;
- }
+ if (mad_send_wr->send_wr.num_sge > 1)
+ return -EINVAL; /* TODO: support num_sge > 1 */
+
+ mad_send_wr->seg_num = 1;
mad_send_wr->newwin = 1;
+ mad_send_wr->data_offset = data_offset(rmpp_mad->mad_hdr.mgmt_class);
+
+ total_len = 0;
+ for (i = 0; i < mad_send_wr->send_wr.num_sge; i++)
+ total_len += mad_send_wr->send_wr.sg_list[i].length;
+
+ mad_send_wr->total_seg = (total_len - mad_send_wr->data_offset) /
+ (sizeof(struct ib_rmpp_mad) - mad_send_wr->data_offset);
+ mad_send_wr->pad = total_len - IB_MGMT_RMPP_HDR -
+ be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
/* We need to wait for the final ACK even if there isn't a response */
mad_send_wr->refcount += (mad_send_wr->timeout == 0);
if (!mad_send_wr->timeout)
return IB_RMPP_RESULT_PROCESSED; /* Response received */
- if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count) {
+ if (mad_send_wr->last_ack == mad_send_wr->total_seg) {
mad_send_wr->timeout =
msecs_to_jiffies(mad_send_wr->send_buf.timeout_ms);
return IB_RMPP_RESULT_PROCESSED; /* Send done */
}
- if (mad_send_wr->seg_num == mad_send_wr->newwin ||
- mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count)
+ if (mad_send_wr->seg_num > mad_send_wr->newwin ||
+ mad_send_wr->seg_num > mad_send_wr->total_seg)
return IB_RMPP_RESULT_PROCESSED; /* Wait for ACK */
ret = send_next_seg(mad_send_wr);
IB_MGMT_RMPP_FLAG_ACTIVE))
return IB_RMPP_RESULT_UNHANDLED; /* RMPP not active */
- if (mad_send_wr->last_ack == mad_send_wr->send_buf.seg_count)
+ if (mad_send_wr->last_ack == mad_send_wr->total_seg)
return IB_RMPP_RESULT_PROCESSED;
- mad_send_wr->seg_num = mad_send_wr->last_ack;
- mad_send_wr->cur_seg = mad_send_wr->last_ack_seg;
-
+ mad_send_wr->seg_num = mad_send_wr->last_ack + 1;
ret = send_next_seg(mad_send_wr);
if (ret)
return IB_RMPP_RESULT_PROCESSED;