RDMA/nes: Handle MPA Reject message properly

While doing testing, there are failures as MPA Reject call is not
handled.  To handle MPA Reject call, following changes are done:

*Handle inbound/outbound MPA Reject response message.
	When nes_reject() is called for pending MPA request reply,
	send the MPA Reject message to its peer (active
	side)cm_node. The peer cm_node (active side) will indicate
	Reject message event for the pending Connect Request.

*Handle MPA Reject response message for loopback connections and listener.
	When MPA Request is rejected, check if it is a loopback
	connection and if it is then it will send Reject message event
	to its peer loopback node. Also when destroying listener,
	check if the cm_nodes for that listener are loopback or not.

*Add gracefull connection close with the MPA Reject response message.
	Send gracefull close (FIN, FIN ACK..) to terminate the cm_nodes.

*Some code re-org while making the above changes.
	Removed recv_list and recv_list_lock from the cm_node
	structure as there can be only one receive close entry on the
	timer. Also implemented handle_recv_entry() as receive close
	entry is processed from both nes_rem_ref_cm_node() as well as
	nes_cm_timer_tick().

Signed-off-by: Faisal Latif <faisal.latif@intel.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
Faisal Latif 2009-03-06 15:15:01 -08:00 committed by Roland Dreier
parent 0145f341a9
commit 9d5ab13325
2 changed files with 403 additions and 195 deletions

View file

@ -103,6 +103,7 @@ static int nes_disconnect(struct nes_qp *nesqp, int abrupt);
static void nes_disconnect_worker(struct work_struct *work);
static int send_mpa_request(struct nes_cm_node *, struct sk_buff *);
static int send_mpa_reject(struct nes_cm_node *);
static int send_syn(struct nes_cm_node *, u32, struct sk_buff *);
static int send_reset(struct nes_cm_node *, struct sk_buff *);
static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb);
@ -113,8 +114,7 @@ static void process_packet(struct nes_cm_node *, struct sk_buff *,
static void active_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void passive_open_err(struct nes_cm_node *, struct sk_buff *, int);
static void cleanup_retrans_entry(struct nes_cm_node *);
static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *,
enum nes_cm_event_type);
static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *);
static void free_retrans_entry(struct nes_cm_node *cm_node);
static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
struct sk_buff *skb, int optionsize, int passive);
@ -124,6 +124,8 @@ static void cm_event_connected(struct nes_cm_event *);
static void cm_event_connect_error(struct nes_cm_event *);
static void cm_event_reset(struct nes_cm_event *);
static void cm_event_mpa_req(struct nes_cm_event *);
static void cm_event_mpa_reject(struct nes_cm_event *);
static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node);
static void print_core(struct nes_cm_core *core);
@ -196,7 +198,6 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
*/
static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
int ret;
if (!skb) {
nes_debug(NES_DBG_CM, "skb set to NULL\n");
return -1;
@ -206,11 +207,27 @@ static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
cm_node->mpa_frame_size, SET_ACK);
ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
if (ret < 0)
return ret;
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
}
return 0;
static int send_mpa_reject(struct nes_cm_node *cm_node)
{
struct sk_buff *skb = NULL;
skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
return -ENOMEM;
}
/* send an MPA reject frame */
form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
cm_node->mpa_frame_size, SET_ACK | SET_FIN);
cm_node->state = NES_CM_STATE_FIN_WAIT1;
return schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
}
@ -218,14 +235,17 @@ static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
* recv_mpa - process a received TCP pkt, we are expecting an
* IETF MPA frame
*/
static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len)
static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
u32 len)
{
struct ietf_mpa_frame *mpa_frame;
*type = NES_MPA_REQUEST_ACCEPT;
/* assume req frame is in tcp data payload */
if (len < sizeof(struct ietf_mpa_frame)) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too small (%x)\n", len);
return -1;
return -EINVAL;
}
mpa_frame = (struct ietf_mpa_frame *)buffer;
@ -234,14 +254,25 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len)
if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
" complete (%x + %x != %x)\n",
cm_node->mpa_frame_size, (u32)sizeof(struct ietf_mpa_frame), len);
return -1;
cm_node->mpa_frame_size,
(u32)sizeof(struct ietf_mpa_frame), len);
return -EINVAL;
}
/* make sure it does not exceed the max size */
if (len > MAX_CM_BUFFER) {
nes_debug(NES_DBG_CM, "The received ietf buffer was too large"
" (%x + %x != %x)\n",
cm_node->mpa_frame_size,
(u32)sizeof(struct ietf_mpa_frame), len);
return -EINVAL;
}
/* copy entire MPA frame to our cm_node's frame */
memcpy(cm_node->mpa_frame_buf, buffer + sizeof(struct ietf_mpa_frame),
cm_node->mpa_frame_size);
if (mpa_frame->flags & IETF_MPA_FLAGS_REJECT)
*type = NES_MPA_REQUEST_REJECT;
return 0;
}
@ -380,7 +411,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
new_send = kzalloc(sizeof(*new_send), GFP_ATOMIC);
if (!new_send)
return -1;
return -ENOMEM;
/* new_send->timetosend = currenttime */
new_send->retrycount = NES_DEFAULT_RETRYS;
@ -394,9 +425,11 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (type == NES_TIMER_TYPE_CLOSE) {
new_send->timetosend += (HZ/10);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
list_add_tail(&new_send->list, &cm_node->recv_list);
spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
if (cm_node->recv_entry) {
WARN_ON(1);
return -EINVAL;
}
cm_node->recv_entry = new_send;
}
if (type == NES_TIMER_TYPE_SEND) {
@ -435,61 +468,34 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
return ret;
}
/**
* nes_cm_timer_tick
*/
static void nes_cm_timer_tick(unsigned long pass)
static void nes_retrans_expired(struct nes_cm_node *cm_node)
{
unsigned long flags, qplockflags;
unsigned long nexttimeout = jiffies + NES_LONG_TIME;
struct iw_cm_id *cm_id;
struct nes_cm_node *cm_node;
struct nes_timer_entry *send_entry, *recv_entry;
struct list_head *list_core, *list_core_temp;
struct list_head *list_node, *list_node_temp;
struct nes_cm_core *cm_core = g_cm_core;
struct nes_qp *nesqp;
u32 settimer = 0;
int ret = NETDEV_TX_OK;
enum nes_cm_node_state last_state;
struct list_head timer_list;
INIT_LIST_HEAD(&timer_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp,
&cm_core->connected_nodes) {
cm_node = container_of(list_node, struct nes_cm_node, list);
if (!list_empty(&cm_node->recv_list) || (cm_node->send_entry)) {
add_ref_cm_node(cm_node);
list_add(&cm_node->timer_entry, &timer_list);
}
}
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp, &timer_list) {
cm_node = container_of(list_node, struct nes_cm_node,
timer_entry);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
list_for_each_safe(list_core, list_node_temp,
&cm_node->recv_list) {
recv_entry = container_of(list_core,
struct nes_timer_entry, list);
if (!recv_entry)
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_CLOSING:
rem_ref_cm_node(cm_node->cm_core, cm_node);
break;
if (time_after(recv_entry->timetosend, jiffies)) {
if (nexttimeout > recv_entry->timetosend ||
!settimer) {
nexttimeout = recv_entry->timetosend;
settimer = 1;
case NES_CM_STATE_LAST_ACK:
case NES_CM_STATE_FIN_WAIT1:
case NES_CM_STATE_MPAREJ_RCVD:
send_reset(cm_node, NULL);
break;
default:
create_event(cm_node, NES_CM_EVENT_ABORTED);
}
continue;
}
list_del(&recv_entry->list);
cm_id = cm_node->cm_id;
spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
}
static void handle_recv_entry(struct nes_cm_node *cm_node, u32 rem_node)
{
struct nes_timer_entry *recv_entry = cm_node->recv_entry;
struct iw_cm_id *cm_id = cm_node->cm_id;
struct nes_qp *nesqp;
unsigned long qplockflags;
if (!recv_entry)
return;
nesqp = (struct nes_qp *)recv_entry->skb;
if (nesqp) {
spin_lock_irqsave(&nesqp->lock, qplockflags);
if (nesqp->cm_id) {
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
@ -500,25 +506,70 @@ static void nes_cm_timer_tick(unsigned long pass)
nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
nesqp->ibqp_state = IB_QPS_ERR;
spin_unlock_irqrestore(&nesqp->lock,
qplockflags);
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_cm_disconn(nesqp);
} else {
spin_unlock_irqrestore(&nesqp->lock,
qplockflags);
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
"refcount = %d: HIT A "
"NES_TIMER_TYPE_CLOSE with nothing "
"to do!!!\n", nesqp->hwqp.qp_id, cm_id,
atomic_read(&nesqp->refcount));
}
if (cm_id)
cm_id->rem_ref(cm_id);
kfree(recv_entry);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
} else if (rem_node) {
/* TIME_WAIT state */
rem_ref_cm_node(cm_node->cm_core, cm_node);
}
if (cm_node->cm_id)
cm_id->rem_ref(cm_id);
kfree(recv_entry);
cm_node->recv_entry = NULL;
}
/**
* nes_cm_timer_tick
*/
static void nes_cm_timer_tick(unsigned long pass)
{
unsigned long flags;
unsigned long nexttimeout = jiffies + NES_LONG_TIME;
struct nes_cm_node *cm_node;
struct nes_timer_entry *send_entry, *recv_entry;
struct list_head *list_core_temp;
struct list_head *list_node;
struct nes_cm_core *cm_core = g_cm_core;
u32 settimer = 0;
int ret = NETDEV_TX_OK;
struct list_head timer_list;
INIT_LIST_HEAD(&timer_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp,
&cm_core->connected_nodes) {
cm_node = container_of(list_node, struct nes_cm_node, list);
if ((cm_node->recv_entry) || (cm_node->send_entry)) {
add_ref_cm_node(cm_node);
list_add(&cm_node->timer_entry, &timer_list);
}
}
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp, &timer_list) {
cm_node = container_of(list_node, struct nes_cm_node,
timer_entry);
recv_entry = cm_node->recv_entry;
if (recv_entry) {
if (time_after(recv_entry->timetosend, jiffies)) {
if (nexttimeout > recv_entry->timetosend ||
!settimer) {
nexttimeout = recv_entry->timetosend;
settimer = 1;
}
} else
handle_recv_entry(cm_node, 1);
}
spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
do {
@ -533,12 +584,11 @@ static void nes_cm_timer_tick(unsigned long pass)
nexttimeout =
send_entry->timetosend;
settimer = 1;
break;
}
} else {
free_retrans_entry(cm_node);
break;
}
break;
}
if ((cm_node->state == NES_CM_STATE_TSA) ||
@ -550,16 +600,12 @@ static void nes_cm_timer_tick(unsigned long pass)
if (!send_entry->retranscount ||
!send_entry->retrycount) {
cm_packets_dropped++;
last_state = cm_node->state;
cm_node->state = NES_CM_STATE_CLOSED;
free_retrans_entry(cm_node);
spin_unlock_irqrestore(
&cm_node->retrans_list_lock, flags);
if (last_state == NES_CM_STATE_SYN_RCVD)
rem_ref_cm_node(cm_core, cm_node);
else
create_event(cm_node,
NES_CM_EVENT_ABORTED);
nes_retrans_expired(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
spin_lock_irqsave(&cm_node->retrans_list_lock,
flags);
break;
@ -714,7 +760,7 @@ static int send_reset(struct nes_cm_node *cm_node, struct sk_buff *skb)
skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
return -1;
return -ENOMEM;
}
form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, flags);
@ -871,7 +917,8 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
struct nes_cm_listener *listener, int free_hanging_nodes)
{
int ret = 1;
int ret = -EINVAL;
int err = 0;
unsigned long flags;
struct list_head *list_pos = NULL;
struct list_head *list_temp = NULL;
@ -901,9 +948,59 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
list_for_each_safe(list_pos, list_temp, &reset_list) {
cm_node = container_of(list_pos, struct nes_cm_node,
reset_entry);
cleanup_retrans_entry(cm_node);
send_reset(cm_node, NULL);
{
struct nes_cm_node *loopback = cm_node->loopbackpartner;
if (NES_CM_STATE_FIN_WAIT1 <= cm_node->state) {
rem_ref_cm_node(cm_node->cm_core, cm_node);
} else {
if (!loopback) {
cleanup_retrans_entry(cm_node);
err = send_reset(cm_node, NULL);
if (err) {
cm_node->state =
NES_CM_STATE_CLOSED;
WARN_ON(1);
} else {
cm_node->state =
NES_CM_STATE_CLOSED;
rem_ref_cm_node(
cm_node->cm_core,
cm_node);
}
} else {
struct nes_cm_event event;
event.cm_node = loopback;
event.cm_info.rem_addr =
loopback->rem_addr;
event.cm_info.loc_addr =
loopback->loc_addr;
event.cm_info.rem_port =
loopback->rem_port;
event.cm_info.loc_port =
loopback->loc_port;
event.cm_info.cm_id = loopback->cm_id;
cm_event_connect_error(&event);
loopback->state = NES_CM_STATE_CLOSED;
event.cm_node = cm_node;
event.cm_info.rem_addr =
cm_node->rem_addr;
event.cm_info.loc_addr =
cm_node->loc_addr;
event.cm_info.rem_port =
cm_node->rem_port;
event.cm_info.loc_port =
cm_node->loc_port;
event.cm_info.cm_id = cm_node->cm_id;
cm_event_reset(&event);
rem_ref_cm_node(cm_node->cm_core,
cm_node);
}
}
}
}
spin_lock_irqsave(&cm_core->listen_list_lock, flags);
@ -964,6 +1061,7 @@ static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
if (cm_node->accept_pend) {
BUG_ON(!cm_node->listener);
atomic_dec(&cm_node->listener->pend_accepts_cnt);
cm_node->accept_pend = 0;
BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
}
@ -990,7 +1088,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
memset(&fl, 0, sizeof fl);
fl.nl_u.ip4_u.daddr = htonl(dst_ip);
if (ip_route_output_key(&init_net, &rt, &fl)) {
printk("%s: ip_route_output_key failed for 0x%08X\n",
printk(KERN_ERR "%s: ip_route_output_key failed for 0x%08X\n",
__func__, dst_ip);
return rc;
}
@ -1053,8 +1151,6 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->cm_id);
spin_lock_init(&cm_node->retrans_list_lock);
INIT_LIST_HEAD(&cm_node->recv_list);
spin_lock_init(&cm_node->recv_list_lock);
cm_node->loopbackpartner = NULL;
atomic_set(&cm_node->ref_count, 1);
@ -1122,10 +1218,7 @@ static int add_ref_cm_node(struct nes_cm_node *cm_node)
static int rem_ref_cm_node(struct nes_cm_core *cm_core,
struct nes_cm_node *cm_node)
{
unsigned long flags, qplockflags;
struct nes_timer_entry *recv_entry;
struct iw_cm_id *cm_id;
struct list_head *list_core, *list_node_temp;
unsigned long flags;
struct nes_qp *nesqp;
if (!cm_node)
@ -1146,38 +1239,9 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
atomic_dec(&cm_node->listener->pend_accepts_cnt);
BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
}
BUG_ON(cm_node->send_entry);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) {
recv_entry = container_of(list_core, struct nes_timer_entry,
list);
list_del(&recv_entry->list);
cm_id = cm_node->cm_id;
spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
nesqp = (struct nes_qp *)recv_entry->skb;
spin_lock_irqsave(&nesqp->lock, qplockflags);
if (nesqp->cm_id) {
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A "
"NES_TIMER_TYPE_CLOSE with something to do!\n",
nesqp->hwqp.qp_id, cm_id);
nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
nesqp->ibqp_state = IB_QPS_ERR;
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_cm_disconn(nesqp);
} else {
spin_unlock_irqrestore(&nesqp->lock, qplockflags);
nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A "
"NES_TIMER_TYPE_CLOSE with nothing to do!\n",
nesqp->hwqp.qp_id, cm_id);
}
cm_id->rem_ref(cm_id);
kfree(recv_entry);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
}
spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
WARN_ON(cm_node->send_entry);
if (cm_node->recv_entry)
handle_recv_entry(cm_node, 0);
if (cm_node->listener) {
mini_cm_dec_refcnt_listen(cm_core, cm_node->listener, 0);
} else {
@ -1262,8 +1326,7 @@ static void drop_packet(struct sk_buff *skb)
dev_kfree_skb_any(skb);
}
static void handle_fin_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
struct tcphdr *tcph)
static void handle_fin_pkt(struct nes_cm_node *cm_node)
{
nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
"refcnt=%d\n", cm_node, cm_node->state,
@ -1275,23 +1338,30 @@ static void handle_fin_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_ESTABLISHED:
case NES_CM_STATE_MPAREQ_SENT:
case NES_CM_STATE_MPAREJ_RCVD:
cm_node->state = NES_CM_STATE_LAST_ACK;
send_fin(cm_node, skb);
send_fin(cm_node, NULL);
break;
case NES_CM_STATE_FIN_WAIT1:
cm_node->state = NES_CM_STATE_CLOSING;
send_ack(cm_node, skb);
send_ack(cm_node, NULL);
/* Wait for ACK as this is simultanous close..
* After we receive ACK, do not send anything..
* Just rm the node.. Done.. */
break;
case NES_CM_STATE_FIN_WAIT2:
cm_node->state = NES_CM_STATE_TIME_WAIT;
send_ack(cm_node, skb);
send_ack(cm_node, NULL);
schedule_nes_timer(cm_node, NULL, NES_TIMER_TYPE_CLOSE, 1, 0);
break;
case NES_CM_STATE_TIME_WAIT:
cm_node->state = NES_CM_STATE_CLOSED;
rem_ref_cm_node(cm_node->cm_core, cm_node);
break;
case NES_CM_STATE_TSA:
default:
nes_debug(NES_DBG_CM, "Error Rcvd FIN for node-%p state = %d\n",
cm_node, cm_node->state);
drop_packet(skb);
break;
}
}
@ -1337,23 +1407,35 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
cleanup_retrans_entry(cm_node);
drop_packet(skb);
break;
case NES_CM_STATE_TIME_WAIT:
cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
rem_ref_cm_node(cm_node->cm_core, cm_node);
drop_packet(skb);
break;
case NES_CM_STATE_FIN_WAIT1:
cleanup_retrans_entry(cm_node);
nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__);
default:
drop_packet(skb);
break;
}
}
static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb,
enum nes_cm_event_type type)
static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
{
int ret;
int ret = 0;
int datasize = skb->len;
u8 *dataloc = skb->data;
ret = parse_mpa(cm_node, dataloc, datasize);
if (ret < 0) {
enum nes_cm_event_type type = NES_CM_EVENT_UNKNOWN;
u32 res_type;
ret = parse_mpa(cm_node, dataloc, &res_type, datasize);
if (ret) {
nes_debug(NES_DBG_CM, "didn't like MPA Request\n");
if (type == NES_CM_EVENT_CONNECTED) {
if (cm_node->state == NES_CM_STATE_MPAREQ_SENT) {
nes_debug(NES_DBG_CM, "%s[%u] create abort for "
"cm_node=%p listener=%p state=%d\n", __func__,
__LINE__, cm_node, cm_node->listener,
@ -1362,18 +1444,38 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb,
} else {
passive_open_err(cm_node, skb, 1);
}
} else {
cleanup_retrans_entry(cm_node);
dev_kfree_skb_any(skb);
if (type == NES_CM_EVENT_CONNECTED)
cm_node->state = NES_CM_STATE_TSA;
else
return;
}
switch (cm_node->state) {
case NES_CM_STATE_ESTABLISHED:
if (res_type == NES_MPA_REQUEST_REJECT) {
/*BIG problem as we are receiving the MPA.. So should
* not be REJECT.. This is Passive Open.. We can
* only receive it Reject for Active Open...*/
WARN_ON(1);
}
cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
type = NES_CM_EVENT_MPA_REQ;
atomic_set(&cm_node->passive_state,
NES_PASSIVE_STATE_INDICATED);
create_event(cm_node, type);
break;
case NES_CM_STATE_MPAREQ_SENT:
if (res_type == NES_MPA_REQUEST_REJECT) {
type = NES_CM_EVENT_MPA_REJECT;
cm_node->state = NES_CM_STATE_MPAREJ_RCVD;
} else {
type = NES_CM_EVENT_CONNECTED;
cm_node->state = NES_CM_STATE_TSA;
}
return ;
break;
default:
WARN_ON(1);
break;
}
dev_kfree_skb_any(skb);
create_event(cm_node, type);
}
static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
@ -1461,8 +1563,6 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
break;
case NES_CM_STATE_LISTENING:
/* Passive OPEN */
cm_node->accept_pend = 1;
atomic_inc(&cm_node->listener->pend_accepts_cnt);
if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
cm_node->listener->backlog) {
nes_debug(NES_DBG_CM, "drop syn due to backlog "
@ -1480,6 +1580,9 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + 1;
BUG_ON(cm_node->send_entry);
cm_node->accept_pend = 1;
atomic_inc(&cm_node->listener->pend_accepts_cnt);
cm_node->state = NES_CM_STATE_SYN_RCVD;
send_syn(cm_node, 1, skb);
break;
@ -1514,6 +1617,7 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
inc_sequence = ntohl(tcph->seq);
switch (cm_node->state) {
case NES_CM_STATE_SYN_SENT:
cleanup_retrans_entry(cm_node);
/* active open */
if (check_syn(cm_node, tcph, skb))
return;
@ -1563,10 +1667,7 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
u32 rem_seq;
int ret;
int optionsize;
u32 temp_seq = cm_node->tcp_cntxt.loc_seq_num;
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
if (check_seq(cm_node, tcph, skb))
return;
@ -1576,7 +1677,7 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
rem_seq = ntohl(tcph->seq);
rem_seq_ack = ntohl(tcph->ack_seq);
datasize = skb->len;
cleanup_retrans_entry(cm_node);
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
/* Passive OPEN */
@ -1584,7 +1685,6 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
if (ret)
break;
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
cm_node->tcp_cntxt.loc_seq_num = temp_seq;
if (cm_node->tcp_cntxt.rem_ack_num !=
cm_node->tcp_cntxt.loc_seq_num) {
nes_debug(NES_DBG_CM, "rem_ack_num != loc_seq_num\n");
@ -1593,10 +1693,10 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
return;
}
cm_node->state = NES_CM_STATE_ESTABLISHED;
cleanup_retrans_entry(cm_node);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_MPA_REQ);
handle_rcv_mpa(cm_node, skb);
} else { /* rcvd ACK only */
dev_kfree_skb_any(skb);
cleanup_retrans_entry(cm_node);
@ -1604,20 +1704,19 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
break;
case NES_CM_STATE_ESTABLISHED:
/* Passive OPEN */
/* We expect mpa frame to be received only */
cleanup_retrans_entry(cm_node);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
handle_rcv_mpa(cm_node, skb,
NES_CM_EVENT_MPA_REQ);
handle_rcv_mpa(cm_node, skb);
} else
drop_packet(skb);
break;
case NES_CM_STATE_MPAREQ_SENT:
cleanup_retrans_entry(cm_node);
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_CONNECTED);
handle_rcv_mpa(cm_node, skb);
} else { /* Could be just an ack pkt.. */
cleanup_retrans_entry(cm_node);
dev_kfree_skb_any(skb);
@ -1628,13 +1727,24 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
cleanup_retrans_entry(cm_node);
send_reset(cm_node, skb);
break;
case NES_CM_STATE_LAST_ACK:
cleanup_retrans_entry(cm_node);
cm_node->state = NES_CM_STATE_CLOSED;
cm_node->cm_id->rem_ref(cm_node->cm_id);
case NES_CM_STATE_CLOSING:
cleanup_retrans_entry(cm_node);
rem_ref_cm_node(cm_node->cm_core, cm_node);
drop_packet(skb);
break;
case NES_CM_STATE_FIN_WAIT1:
cleanup_retrans_entry(cm_node);
drop_packet(skb);
cm_node->state = NES_CM_STATE_FIN_WAIT2;
break;
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_FIN_WAIT2:
case NES_CM_STATE_TSA:
case NES_CM_STATE_MPAREQ_RCVD:
case NES_CM_STATE_LAST_ACK:
case NES_CM_STATE_CLOSING:
case NES_CM_STATE_UNKNOWN:
default:
drop_packet(skb);
@ -1744,6 +1854,7 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
{
enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
struct tcphdr *tcph = tcp_hdr(skb);
u32 fin_set = 0;
skb_pull(skb, ip_hdr(skb)->ihl << 2);
nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
@ -1756,10 +1867,10 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
pkt_type = NES_PKT_TYPE_SYN;
if (tcph->ack)
pkt_type = NES_PKT_TYPE_SYNACK;
} else if (tcph->fin)
pkt_type = NES_PKT_TYPE_FIN;
else if (tcph->ack)
} else if (tcph->ack)
pkt_type = NES_PKT_TYPE_ACK;
if (tcph->fin)
fin_set = 1;
switch (pkt_type) {
case NES_PKT_TYPE_SYN:
@ -1770,15 +1881,16 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
break;
case NES_PKT_TYPE_ACK:
handle_ack_pkt(cm_node, skb, tcph);
if (fin_set)
handle_fin_pkt(cm_node);
break;
case NES_PKT_TYPE_RST:
handle_rst_pkt(cm_node, skb, tcph);
break;
case NES_PKT_TYPE_FIN:
handle_fin_pkt(cm_node, skb, tcph);
break;
default:
drop_packet(skb);
if (fin_set)
handle_fin_pkt(cm_node);
break;
}
}
@ -1921,7 +2033,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
loopbackremotenode->tcp_cntxt.rcv_wscale;
loopbackremotenode->tcp_cntxt.snd_wscale =
cm_node->tcp_cntxt.rcv_wscale;
loopbackremotenode->state = NES_CM_STATE_MPAREQ_RCVD;
create_event(loopbackremotenode, NES_CM_EVENT_MPA_REQ);
}
return cm_node;
@ -1976,7 +2088,11 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
{
int ret = 0;
int err = 0;
int passive_state;
struct nes_cm_event event;
struct iw_cm_id *cm_id = cm_node->cm_id;
struct nes_cm_node *loopback = cm_node->loopbackpartner;
nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n",
__func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
@ -1985,12 +2101,38 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
return ret;
cleanup_retrans_entry(cm_node);
if (!loopback) {
passive_state = atomic_add_return(1, &cm_node->passive_state);
if (passive_state == NES_SEND_RESET_EVENT) {
cm_node->state = NES_CM_STATE_CLOSED;
if (passive_state == NES_SEND_RESET_EVENT)
rem_ref_cm_node(cm_core, cm_node);
else
ret = send_reset(cm_node, NULL);
} else {
ret = send_mpa_reject(cm_node);
if (ret) {
cm_node->state = NES_CM_STATE_CLOSED;
err = send_reset(cm_node, NULL);
if (err)
WARN_ON(1);
} else
cm_id->add_ref(cm_id);
}
} else {
cm_node->cm_id = NULL;
event.cm_node = loopback;
event.cm_info.rem_addr = loopback->rem_addr;
event.cm_info.loc_addr = loopback->loc_addr;
event.cm_info.rem_port = loopback->rem_port;
event.cm_info.loc_port = loopback->loc_port;
event.cm_info.cm_id = loopback->cm_id;
cm_event_mpa_reject(&event);
rem_ref_cm_node(cm_core, cm_node);
loopback->state = NES_CM_STATE_CLOSING;
cm_id = loopback->cm_id;
rem_ref_cm_node(cm_core, loopback);
cm_id->rem_ref(cm_id);
}
return ret;
}
@ -2027,6 +2169,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
case NES_CM_STATE_CLOSING:
ret = -1;
break;
case NES_CM_STATE_MPAREJ_RCVD:
case NES_CM_STATE_LISTENING:
case NES_CM_STATE_UNKNOWN:
case NES_CM_STATE_INITED:
@ -2621,9 +2764,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
NES_QPCONTEXT_ORDIRD_WRPDU);
} else {
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
NES_QPCONTEXT_ORDIRD_WRPDU |
NES_QPCONTEXT_ORDIRD_ALSMM));
cpu_to_le32(NES_QPCONTEXT_ORDIRD_WRPDU);
}
nesqp->skip_lsmm = 1;
@ -2745,23 +2886,35 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
{
struct nes_cm_node *cm_node;
struct nes_cm_node *loopback;
struct nes_cm_core *cm_core;
atomic_inc(&cm_rejects);
cm_node = (struct nes_cm_node *) cm_id->provider_data;
loopback = cm_node->loopbackpartner;
cm_core = cm_node->cm_core;
cm_node->cm_id = cm_id;
cm_node->mpa_frame_size = sizeof(struct ietf_mpa_frame) + pdata_len;
strcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP);
memcpy(&cm_node->mpa_frame.priv_data, pdata, pdata_len);
if (cm_node->mpa_frame_size > MAX_CM_BUFFER)
return -EINVAL;
strcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP);
if (loopback) {
memcpy(&loopback->mpa_frame.priv_data, pdata, pdata_len);
loopback->mpa_frame.priv_data_len = pdata_len;
loopback->mpa_frame_size = sizeof(struct ietf_mpa_frame) +
pdata_len;
} else {
memcpy(&cm_node->mpa_frame.priv_data, pdata, pdata_len);
cm_node->mpa_frame.priv_data_len = cpu_to_be16(pdata_len);
}
cm_node->mpa_frame.rev = mpa_version;
cm_node->mpa_frame.flags = IETF_MPA_FLAGS_CRC | IETF_MPA_FLAGS_REJECT;
cm_core->api->reject(cm_core, &cm_node->mpa_frame, cm_node);
return 0;
return cm_core->api->reject(cm_core, &cm_node->mpa_frame, cm_node);
}
@ -3270,13 +3423,56 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
cm_event.remote_addr.sin_family = AF_INET;
cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port);
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
__func__, __LINE__, ret);
return;
}
static void cm_event_mpa_reject(struct nes_cm_event *event)
{
struct iw_cm_id *cm_id;
struct iw_cm_event cm_event;
struct nes_cm_node *cm_node;
int ret;
cm_node = event->cm_node;
if (!cm_node)
return;
cm_id = cm_node->cm_id;
atomic_inc(&cm_connect_reqs);
nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
cm_event.status = -ECONNREFUSED;
cm_event.provider_data = cm_id->provider_data;
cm_event.local_addr.sin_family = AF_INET;
cm_event.local_addr.sin_port = htons(event->cm_info.loc_port);
cm_event.local_addr.sin_addr.s_addr = htonl(event->cm_info.loc_addr);
cm_event.remote_addr.sin_family = AF_INET;
cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port);
cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
nes_debug(NES_DBG_CM, "call CM_EVENT_MPA_REJECTED, local_addr=%08x, "
"remove_addr=%08x\n",
cm_event.local_addr.sin_addr.s_addr,
cm_event.remote_addr.sin_addr.s_addr);
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, ret=%d\n",
__func__, __LINE__, ret);
return;
@ -3341,6 +3537,14 @@ static void nes_cm_event_handler(struct work_struct *work)
cm_event_connected(event);
nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
break;
case NES_CM_EVENT_MPA_REJECT:
if ((!event->cm_node->cm_id) ||
(event->cm_node->state == NES_CM_STATE_TSA))
break;
cm_event_mpa_reject(event);
nes_debug(NES_DBG_CM, "CM Event: REJECT\n");
break;
case NES_CM_EVENT_ABORTED:
if ((!event->cm_node->cm_id) ||
(event->cm_node->state == NES_CM_STATE_TSA))

View file

@ -39,6 +39,9 @@
#define NES_MANAGE_APBVT_DEL 0
#define NES_MANAGE_APBVT_ADD 1
#define NES_MPA_REQUEST_ACCEPT 1
#define NES_MPA_REQUEST_REJECT 2
/* IETF MPA -- defines, enums, structs */
#define IEFT_MPA_KEY_REQ "MPA ID Req Frame"
#define IEFT_MPA_KEY_REP "MPA ID Rep Frame"
@ -186,6 +189,7 @@ enum nes_cm_node_state {
NES_CM_STATE_ACCEPTING,
NES_CM_STATE_MPAREQ_SENT,
NES_CM_STATE_MPAREQ_RCVD,
NES_CM_STATE_MPAREJ_RCVD,
NES_CM_STATE_TSA,
NES_CM_STATE_FIN_WAIT1,
NES_CM_STATE_FIN_WAIT2,
@ -278,13 +282,12 @@ struct nes_cm_node {
struct nes_timer_entry *send_entry;
spinlock_t retrans_list_lock;
struct list_head recv_list;
spinlock_t recv_list_lock;
struct nes_timer_entry *recv_entry;
int send_write0;
union {
struct ietf_mpa_frame mpa_frame;
u8 mpa_frame_buf[NES_CM_DEFAULT_MTU];
u8 mpa_frame_buf[MAX_CM_BUFFER];
};
u16 mpa_frame_size;
struct iw_cm_id *cm_id;
@ -326,6 +329,7 @@ enum nes_cm_event_type {
NES_CM_EVENT_MPA_REQ,
NES_CM_EVENT_MPA_CONNECT,
NES_CM_EVENT_MPA_ACCEPT,
NES_CM_EVENT_MPA_REJECT,
NES_CM_EVENT_MPA_ESTABLISHED,
NES_CM_EVENT_CONNECTED,
NES_CM_EVENT_CLOSED,