struct work_struct work;
wait_queue_head_t wq;
- struct delayed_work time_wait_work;
+ struct delayed_work dreq_wait_work;
struct work_struct destroy_work;
/* Like tcp_sock */
int xmit_size_goal;
int nonagle;
- int time_wait;
+ int dreq_wait_timeout;
unsigned keepalive_time;
+ spinlock_t lock;
+
/* tx_head/rx_head when keepalive timer started */
unsigned keepalive_tx_head;
unsigned keepalive_rx_head;
+ int destructed_already;
+ int sdp_disconnect;
+
/* Data below will be reset on error */
/* rdma specific */
struct rdma_cm_id *id;
return (struct sdp_sock *)sk;
}
-static inline void sdp_set_state(struct sock *sk, int state)
+static inline char *sdp_state_str(int state)
+{
+ static char *state_str[] = {
+ [TCP_ESTABLISHED] = "TCP_ESTABLISHED",
+ [TCP_SYN_SENT] = "TCP_SYN_SENT",
+ [TCP_SYN_RECV] = "TCP_SYN_RECV",
+ [TCP_FIN_WAIT1] = "TCP_FIN_WAIT1",
+ [TCP_FIN_WAIT2] = "TCP_FIN_WAIT2",
+ [TCP_TIME_WAIT] = "TCP_TIME_WAIT",
+ [TCP_CLOSE] = "TCP_CLOSE",
+ [TCP_CLOSE_WAIT] = "TCP_CLOSE_WAIT",
+ [TCP_LAST_ACK] = "TCP_LAST_ACK",
+ [TCP_LISTEN] = "TCP_LISTEN",
+ [TCP_CLOSING] = "TCP_CLOSING",
+ };
+
+ if (state < 0 || state >= TCP_MAX_STATES)
+ return "unknown";
+
+ return state_str[state];
+}
+
+static inline int _sdp_exch_state(const char *func, int line, struct sock *sk,
+ int from_states, int state)
{
+ unsigned long flags;
+ int old;
+
+ spin_lock_irqsave(&sdp_sk(sk)->lock, flags);
+
+ sdp_dbg(sk, "%s:%d - set state: %s -> %s 0x%x\n", __func__, __LINE__,
+ sdp_state_str(sk->sk_state), sdp_state_str(state), from_states);
+
+ if ((1 << sk->sk_state) & ~from_states) {
+ sdp_warn(sk, "trying to exchange state from unexpected state "
+ "%s to state %s. expected states: 0x%x\n",
+ sdp_state_str(sk->sk_state), sdp_state_str(state),
+ from_states);
+ }
+
+ old = sk->sk_state;
sk->sk_state = state;
+
+ spin_unlock_irqrestore(&sdp_sk(sk)->lock, flags);
+
+ return old;
}
+#define sdp_exch_state(sk, from_states, state) \
+ _sdp_exch_state(__func__, __LINE__, sk, from_states, state)
static inline void sdp_set_error(struct sock *sk, int err)
{
+ int ib_teardown_states = TCPF_FIN_WAIT1 | TCPF_CLOSE_WAIT
+ | TCPF_LAST_ACK;
sk->sk_err = -err;
if (sk->sk_socket)
- sk->sk_socket->state = SS_UNCONNECTED;
+ sk->sk_socket->state = SS_DISCONNECTING;
- sdp_set_state(sk, TCP_CLOSE);
-
- if (sdp_sk(sk)->time_wait) {
- sdp_dbg(sk, "%s: destroy in time wait state\n", __func__);
- sdp_sk(sk)->time_wait = 0;
- queue_work(sdp_workqueue, &sdp_sk(sk)->destroy_work);
- }
+ if ((1 << sk->sk_state) & ib_teardown_states)
+ sdp_exch_state(sk, ib_teardown_states, TCP_TIME_WAIT);
+ else
+ sdp_exch_state(sk, ~0, TCP_CLOSE);
sk->sk_error_report(sk);
}
int sdp_cma_handler(struct rdma_cm_id *, struct rdma_cm_event *);
void sdp_reset(struct sock *sk);
void sdp_reset_sk(struct sock *sk, int rc);
-void sdp_time_wait_destroy_sk(struct sdp_sock *ssk);
void sdp_completion_handler(struct ib_cq *cq, void *cq_context);
void sdp_work(struct work_struct *work);
int sdp_post_credits(struct sdp_sock *ssk);
int sdp_poll_cq(struct sdp_sock *ssk, struct ib_cq *cq);
void sdp_post_sends(struct sdp_sock *ssk, int nonagle);
void sdp_destroy_work(struct work_struct *work);
-void sdp_time_wait_work(struct work_struct *work);
+void sdp_cancel_dreq_wait_timeout(struct sdp_sock *ssk);
+void sdp_dreq_wait_timeout_work(struct work_struct *work);
struct sk_buff *sdp_recv_completion(struct sdp_sock *ssk, int id);
struct sk_buff *sdp_send_completion(struct sdp_sock *ssk, int mseq);
void sdp_urg(struct sdp_sock *ssk, struct sk_buff *skb);
}
}
-/* Like tcp_fin */
+/* Like tcp_fin - called when SDP_MID_DISCONNECT is received */
static void sdp_fin(struct sock *sk)
{
sdp_dbg(sk, "%s\n", __func__);
sk->sk_shutdown |= RCV_SHUTDOWN;
sock_set_flag(sk, SOCK_DONE);
+ switch (sk->sk_state) {
+ case TCP_SYN_RECV:
+ case TCP_ESTABLISHED:
+ sdp_exch_state(sk, TCPF_SYN_RECV | TCPF_ESTABLISHED,
+ TCP_CLOSE_WAIT);
+ break;
+
+ case TCP_FIN_WAIT1:
+ /* Received a reply FIN - start Infiniband tear down */
+ sdp_dbg(sk, "%s: Starting Infiniband tear down sending DREQ\n",
+ __func__);
+ sdp_exch_state(sk, TCPF_FIN_WAIT1, TCP_TIME_WAIT);
+
+ if (sdp_sk(sk)->id) {
+ rdma_disconnect(sdp_sk(sk)->id);
+ } else {
+ sdp_warn(sk, "%s: sdp_sk(sk)->id is NULL\n", __func__);
+ BUG();
+ }
+ break;
+ case TCP_TIME_WAIT:
+ /* This is a mutual close situation and we've got the DREQ from
+ the peer before the SDP_MID_DISCONNECT */
+ break;
+ default:
+ sdp_warn(sk, "%s: FIN in unexpected state. sk->sk_state=%d\n",
+ __func__, sk->sk_state);
+ break;
+ }
+
sk_mem_reclaim(sk);
{
int skb_len;
struct sdp_sock *ssk = sdp_sk(sk);
- struct sk_buff *tail;
+ struct sk_buff *tail = NULL;
/* not needed since sk_rmem_alloc is not currently used
* TODO - remove this?
if (unlikely(c < ssk->rx_head - ssk->rx_tail) &&
likely(ssk->bufs > 1) &&
- likely(ssk->tx_head - ssk->tx_tail < SDP_TX_SIZE)) {
+ likely(ssk->tx_head - ssk->tx_tail < SDP_TX_SIZE) &&
+ likely((1 << ssk->isk.sk.sk_state) &
+ (TCPF_ESTABLISHED | TCPF_FIN_WAIT1))) {
skb = sdp_stream_alloc_skb(&ssk->isk.sk,
sizeof(struct sdp_bsdh),
GFP_KERNEL);
sdp_post_send(ssk, skb, SDP_MID_DATA);
}
- if (unlikely((1 << ssk->isk.sk.sk_state) &
- (TCPF_FIN_WAIT1 | TCPF_LAST_ACK)) &&
+ if (unlikely(ssk->sdp_disconnect) &&
!ssk->isk.sk.sk_send_head &&
ssk->bufs > (ssk->remote_credits >= ssk->rx_head - ssk->rx_tail)) {
+ ssk->sdp_disconnect = 0;
skb = sdp_stream_alloc_skb(&ssk->isk.sk,
sizeof(struct sdp_bsdh),
gfp_page);
/* FIXME */
BUG_ON(!skb);
sdp_post_send(ssk, skb, SDP_MID_DISCONN);
- if (ssk->isk.sk.sk_state == TCP_FIN_WAIT1)
- sdp_set_state(&ssk->isk.sk, TCP_FIN_WAIT2);
- else
- sdp_set_state(&ssk->isk.sk, TCP_CLOSING);
}
}
static int sdp_handle_recv_comp(struct sdp_sock *ssk, struct ib_wc *wc)
{
+ struct sock *sk = &ssk->isk.sk;
int frags;
struct sk_buff *skb;
struct sdp_bsdh *h;
if (unlikely(wc->status)) {
if (wc->status != IB_WC_WR_FLUSH_ERR) {
- sdp_dbg(&ssk->isk.sk,
- "Recv completion with error. "
- "Status %d\n", wc->status);
- sdp_reset(&ssk->isk.sk);
+ sdp_dbg(sk, "Recv completion with error. Status %d\n",
+ wc->status);
+ sdp_reset(sk);
}
__kfree_skb(skb);
return 0;
}
- sdp_dbg_data(&ssk->isk.sk, "Recv completion. ID %d Length %d\n",
+ sdp_dbg_data(sk, "Recv completion. ID %d Length %d\n",
(int)wc->wr_id, wc->byte_len);
if (unlikely(wc->byte_len < sizeof(struct sdp_bsdh))) {
printk(KERN_WARNING "SDP BUG! byte_len %d < %zd\n",
}
if (unlikely(h->flags & SDP_OOB_PEND))
- sk_send_sigurg(&ssk->isk.sk);
+ sk_send_sigurg(sk);
skb_pull(skb, sizeof(struct sdp_bsdh));
__kfree_skb(skb);
break;
}
- skb = sdp_sock_queue_rcv_skb(&ssk->isk.sk, skb);
+
+ if (unlikely(sk->sk_shutdown & RCV_SHUTDOWN)) {
+ /* got data in RCV_SHUTDOWN */
+ if (sk->sk_state == TCP_FIN_WAIT1) {
+ /* go into abortive close */
+ sdp_exch_state(sk, TCPF_FIN_WAIT1,
+ TCP_TIME_WAIT);
+
+ sk->sk_prot->disconnect(sk, 0);
+ }
+
+ __kfree_skb(skb);
+ break;
+ }
+ skb = sdp_sock_queue_rcv_skb(sk, skb);
if (unlikely(h->flags & SDP_OOB_PRES))
sdp_urg(ssk, skb);
break;
case SDP_MID_DISCONN:
- /* this will wake recvmsg */
- sdp_sock_queue_rcv_skb(&ssk->isk.sk, skb);
- sdp_fin(&ssk->isk.sk);
+ __kfree_skb(skb);
+ sdp_fin(sk);
break;
case SDP_MID_CHRCVBUF:
sdp_handle_resize_request(ssk,
- (struct sdp_chrecvbuf *)skb->data);
+ (struct sdp_chrecvbuf *)skb->data);
__kfree_skb(skb);
break;
-
case SDP_MID_CHRCVBUF_ACK:
sdp_handle_resize_ack(ssk, (struct sdp_chrecvbuf *)skb->data);
__kfree_skb(skb);
printk(KERN_WARNING "SDP: FIXME MID %d\n", h->mid);
__kfree_skb(skb);
}
+
return 0;
}
static int sdp_handle_send_comp(struct sdp_sock *ssk, struct ib_wc *wc)
{
struct sk_buff *skb;
+ struct sdp_bsdh *h;
skb = sdp_send_completion(ssk, wc->wr_id);
if (unlikely(!skb))
return -1;
- sk_wmem_free_skb(&ssk->isk.sk, skb);
+
if (unlikely(wc->status)) {
if (wc->status != IB_WC_WR_FLUSH_ERR) {
- sdp_dbg(&ssk->isk.sk,
- "Send completion with error. "
- "Status %d\n", wc->status);
- sdp_set_error(&ssk->isk.sk, -ECONNRESET);
+ struct sock *sk = &ssk->isk.sk;
+ sdp_dbg(sk, "Send completion with error. "
+ "Status %d\n", wc->status);
+ sdp_set_error(sk, -ECONNRESET);
wake_up(&ssk->wq);
+
+ queue_work(sdp_workqueue, &ssk->destroy_work);
}
+ goto out;
+ }
+
+ h = (struct sdp_bsdh *)skb->data;
+
+ if (likely(h->mid != SDP_MID_DISCONN))
+ goto out;
+
+ if ((1 << ssk->isk.sk.sk_state) & ~(TCPF_FIN_WAIT1 | TCPF_LAST_ACK)) {
+ sdp_dbg(&ssk->isk.sk,
+ "%s: sent DISCONNECT from unexpected state %d\n",
+ __func__, ssk->isk.sk.sk_state);
}
+out:
+ sk_wmem_free_skb(&ssk->isk.sk, skb);
+
return 0;
}
return;
}
-
- if (ssk->time_wait && !ssk->isk.sk.sk_send_head &&
- ssk->tx_head == ssk->tx_tail) {
- sdp_dbg(&ssk->isk.sk, "%s: destroy in time wait state\n",
- __func__);
- sdp_time_wait_destroy_sk(ssk);
- }
}
void sdp_completion_handler(struct ib_cq *cq, void *cq_context)
sdp_add_sock(sdp_sk(child));
INIT_LIST_HEAD(&sdp_sk(child)->accept_queue);
INIT_LIST_HEAD(&sdp_sk(child)->backlog_queue);
- INIT_DELAYED_WORK(&sdp_sk(child)->time_wait_work, sdp_time_wait_work);
+ INIT_DELAYED_WORK(&sdp_sk(child)->dreq_wait_work, sdp_dreq_wait_timeout_work);
INIT_WORK(&sdp_sk(child)->destroy_work, sdp_destroy_work);
dst_addr = (struct sockaddr_in *)&id->route.addr.dst_addr;
list_add_tail(&sdp_sk(child)->backlog_queue, &sdp_sk(sk)->backlog_queue);
sdp_sk(child)->parent = sk;
- sdp_set_state(child, TCP_SYN_RECV);
+ sdp_exch_state(child, TCPF_LISTEN | TCPF_CLOSE, TCP_SYN_RECV);
/* child->sk_write_space(child); */
/* child->sk_data_ready(child, 0); */
struct sockaddr_in *dst_addr;
sdp_dbg(sk, "%s\n", __func__);
- sdp_set_state(sk, TCP_ESTABLISHED);
+ sdp_exch_state(sk, TCPF_SYN_SENT, TCP_ESTABLISHED);
if (sock_flag(sk, SOCK_KEEPOPEN))
sdp_start_keepalive_timer(sk);
parent = sdp_sk(sk)->parent;
BUG_ON(!parent);
- sdp_set_state(sk, TCP_ESTABLISHED);
+ sdp_exch_state(sk, TCPF_SYN_RECV, TCP_ESTABLISHED);
if (sock_flag(sk, SOCK_KEEPOPEN))
sdp_start_keepalive_timer(sk);
((struct sockaddr_in *)&id->route.addr.src_addr)->sin_addr.s_addr;
rc = sdp_connected_handler(sk, event);
break;
- case RDMA_CM_EVENT_DISCONNECTED:
+ case RDMA_CM_EVENT_DISCONNECTED: /* This means DREQ/DREP received */
sdp_dbg(sk, "RDMA_CM_EVENT_DISCONNECTED\n");
+
+ if (sk->sk_state == TCP_LAST_ACK) {
+ if (sdp_sk(sk)->dreq_wait_timeout)
+ sdp_cancel_dreq_wait_timeout(sdp_sk(sk));
+
+ sdp_exch_state(sk, TCPF_LAST_ACK, TCP_TIME_WAIT);
+
+ sdp_dbg(sk, "%s: waiting for Infiniband tear down\n",
+ __func__);
+ }
+
rdma_disconnect(id);
+
+ if (sk->sk_state != TCP_TIME_WAIT) {
+ sdp_set_error(sk, EPIPE);
+ rc = sdp_disconnected_handler(sk);
+ }
+ break;
+ case RDMA_CM_EVENT_TIMWAIT_EXIT:
+ sdp_dbg(sk, "RDMA_CM_EVENT_TIMEWAIT_EXIT\n");
rc = sdp_disconnected_handler(sk);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
sdp_dbg(sk, "%s event %d handled\n", __func__, event->event);
if (rc && sdp_sk(sk)->id == id) {
+ if (sk->sk_state == TCP_SYN_RECV) {
+ /* sdp_close() will not be called therefore we need
+ to take a refernce till infiniband teardown is
+ finished */
+ sock_hold(sk);
+ }
child = sk;
sdp_sk(sk)->id = NULL;
id->qp = NULL;
memset((void *)&ssk->id, 0, sizeof(*ssk) - offsetof(typeof(*ssk), id));
- if (ssk->time_wait) {
- sdp_dbg(sk, "%s: destroy in time wait state\n", __func__);
- sdp_time_wait_destroy_sk(ssk);
- }
-
sk->sk_state_change(sk);
+ queue_work(sdp_workqueue, &ssk->destroy_work);
+
read_unlock(&device_removal_lock);
}
struct sdp_sock *s, *t;
sdp_dbg(sk, "%s\n", __func__);
+ if (ssk->destructed_already) {
+ sdp_warn(sk, "redestructing sk!");
+ return;
+ }
+
+ ssk->destructed_already = 1;
sdp_remove_sock(ssk);
sdp_dbg(sk, "%s done\n", __func__);
}
-static void sdp_send_active_reset(struct sock *sk, gfp_t priority)
+static void sdp_send_disconnect(struct sock *sk)
{
- sk->sk_prot->disconnect(sk, 0);
+ sdp_sk(sk)->sdp_disconnect = 1;
+ sdp_post_sends(sdp_sk(sk), 0);
}
/*
* State processing on a close.
- * TCP_ESTABLISHED -> TCP_FIN_WAIT1 -> TCP_FIN_WAIT2 -> TCP_CLOSE
+ * TCP_ESTABLISHED -> TCP_FIN_WAIT1 -> TCP_CLOSE
*/
static int sdp_close_state(struct sock *sk)
return 0;
if (sk->sk_state == TCP_ESTABLISHED)
- sdp_set_state(sk, TCP_FIN_WAIT1);
- else if (sk->sk_state == TCP_CLOSE_WAIT)
- sdp_set_state(sk, TCP_LAST_ACK);
- else
+ sdp_exch_state(sk, TCPF_ESTABLISHED, TCP_FIN_WAIT1);
+ else if (sk->sk_state == TCP_CLOSE_WAIT) {
+ sdp_exch_state(sk, TCPF_CLOSE_WAIT, TCP_LAST_ACK);
+
+ sdp_sk(sk)->dreq_wait_timeout = 1;
+ queue_delayed_work(sdp_workqueue, &sdp_sk(sk)->dreq_wait_work,
+ TCP_FIN_TIMEOUT);
+ } else
return 0;
return 1;
}
sdp_delete_keepalive_timer(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
+
+ if ((1 << sk->sk_state) & (TCPF_TIME_WAIT | TCPF_CLOSE)) {
+ /* this could happen if socket was closed by a CM teardown
+ and after that the user called close() */
+ goto out;
+ }
+
if (sk->sk_state == TCP_LISTEN || sk->sk_state == TCP_SYN_SENT) {
- sdp_set_state(sk, TCP_CLOSE);
+ sdp_exch_state(sk, TCPF_LISTEN | TCPF_SYN_SENT, TCP_CLOSE);
/* Special case: stop listening.
This is done by sdp_destruct. */
goto adjudge_to_death;
}
+ sock_hold(sk);
+
/* We need to flush the recv. buffs. We do this only on the
* descriptor close, not protocol-sourced closes, because the
* reader process may not have drained the data yet!
* the FTP client, wheee... Note: timeout is always zero
* in such a case.
*/
- if (data_was_unread) {
+ if (data_was_unread ||
+ (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(LINUX_MIB_TCPABORTONCLOSE);
- sdp_set_state(sk, TCP_CLOSE);
- sdp_send_active_reset(sk, GFP_KERNEL);
- } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
- /* Check zero linger _after_ checking for unread data. */
+ sdp_exch_state(sk, TCPF_CLOSE_WAIT | TCPF_ESTABLISHED,
+ TCP_TIME_WAIT);
+
+ /* Go into abortive close */
sk->sk_prot->disconnect(sk, 0);
- NET_INC_STATS_USER(LINUX_MIB_TCPABORTONDATA);
} else if (sdp_close_state(sk)) {
/* We FIN if the application ate all the data before
* zapping the connection.
*/
- sdp_post_sends(sdp_sk(sk), 0);
+ sdp_send_disconnect(sk);
}
/* TODO: state should move to CLOSE or CLOSE_WAIT etc on disconnect.
Since it currently doesn't, do it here to avoid blocking below. */
if (!sdp_sk(sk)->id)
- sdp_set_state(sk, TCP_CLOSE);
+ sdp_exch_state(sk, TCPF_FIN_WAIT1 | TCPF_LAST_ACK |
+ TCPF_CLOSE_WAIT, TCP_CLOSE);
sk_stream_wait_close(sk, timeout);
*/
lock_sock(sk);
- sock_hold(sk);
sock_orphan(sk);
/* This is a (useful) BSD violating of the RFC. There is a
* consume significant resources. Let's do it with special
* linger2 option. --ANK
*/
-
- if (sk->sk_state == TCP_FIN_WAIT2 &&
- !sk->sk_send_head &&
- sdp_sk(sk)->tx_head == sdp_sk(sk)->tx_tail) {
- sdp_set_state(sk, TCP_CLOSE);
- }
-
- if ((1 << sk->sk_state) & (TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2)) {
- sdp_sk(sk)->time_wait = 1;
+ if (sk->sk_state == TCP_FIN_WAIT1) {
/* TODO: liger2 unimplemented.
We should wait 3.5 * rto. How do I know rto? */
/* TODO: tcp_fin_time to get timeout */
sdp_dbg(sk, "%s: entering time wait refcnt %d\n", __func__,
atomic_read(&sk->sk_refcnt));
atomic_inc(sk->sk_prot->orphan_count);
- queue_delayed_work(sdp_workqueue, &sdp_sk(sk)->time_wait_work,
- TCP_FIN_TIMEOUT);
- goto out;
}
/* TODO: limit number of orphaned sockets.
TCP has sysctl_tcp_mem and sysctl_tcp_max_orphans */
- sock_put(sk);
- /* Otherwise, socket is reprieved until protocol close. */
out:
- sdp_dbg(sk, "%s: last socket put %d\n", __func__,
- atomic_read(&sk->sk_refcnt));
release_sock(sk);
+
sk_common_release(sk);
}
return rc;
}
- sdp_set_state(sk, TCP_SYN_SENT);
+ sdp_exch_state(sk, TCPF_CLOSE, TCP_SYN_SENT);
return 0;
}
struct rdma_cm_id *id;
sdp_dbg(sk, "%s\n", __func__);
- if (ssk->id)
- rc = rdma_disconnect(ssk->id);
- if (old_state != TCP_LISTEN)
+ if (old_state != TCP_LISTEN) {
+ if (ssk->id)
+ rc = rdma_disconnect(ssk->id);
+
return rc;
+ }
- sdp_set_state(sk, TCP_CLOSE);
+ sdp_exch_state(sk, TCPF_LISTEN, TCP_CLOSE);
id = ssk->id;
ssk->id = NULL;
release_sock(sk); /* release socket since locking semantics is parent
return put_user(answ, (int __user *)arg);
}
+void sdp_cancel_dreq_wait_timeout(struct sdp_sock *ssk)
+{
+ ssk->dreq_wait_timeout = 0;
+ cancel_delayed_work(&ssk->dreq_wait_work);
+ atomic_dec(ssk->isk.sk.sk_prot->orphan_count);
+}
+
void sdp_destroy_work(struct work_struct *work)
{
struct sdp_sock *ssk = container_of(work, struct sdp_sock, destroy_work);
struct sock *sk = &ssk->isk.sk;
sdp_dbg(sk, "%s: refcnt %d\n", __func__, atomic_read(&sk->sk_refcnt));
- cancel_delayed_work(&sdp_sk(sk)->time_wait_work);
- atomic_dec(sk->sk_prot->orphan_count);
+ if (ssk->dreq_wait_timeout)
+ sdp_cancel_dreq_wait_timeout(ssk);
- sock_put(sk);
+ if (sk->sk_state == TCP_TIME_WAIT)
+ sock_put(sk);
+
+ /* In normal close current state is TCP_TIME_WAIT or TCP_CLOSE
+ but if a CM connection is dropped below our legs state could
+ be any state */
+ sdp_exch_state(sk, ~0, TCP_CLOSE);
}
-void sdp_time_wait_work(struct work_struct *work)
+void sdp_dreq_wait_timeout_work(struct work_struct *work)
{
- struct sdp_sock *ssk = container_of(work, struct sdp_sock, time_wait_work.work);
+ struct sdp_sock *ssk =
+ container_of(work, struct sdp_sock, dreq_wait_work.work);
struct sock *sk = &ssk->isk.sk;
+
lock_sock(sk);
- sdp_dbg(sk, "%s\n", __func__);
- if (!sdp_sk(sk)->time_wait) {
+ if (!sdp_sk(sk)->dreq_wait_timeout) {
release_sock(sk);
return;
}
- sdp_dbg(sk, "%s: refcnt %d\n", __func__, atomic_read(&sk->sk_refcnt));
+ sdp_dbg(sk, "%s: timed out waiting for DREQ\n", __func__);
- sdp_set_state(sk, TCP_CLOSE);
- sdp_sk(sk)->time_wait = 0;
- release_sock(sk);
+ sdp_sk(sk)->dreq_wait_timeout = 0;
+ sdp_exch_state(sk, TCPF_LAST_ACK, TCP_TIME_WAIT);
- atomic_dec(sk->sk_prot->orphan_count);
- sock_put(sk);
-}
+ release_sock(sk);
-void sdp_time_wait_destroy_sk(struct sdp_sock *ssk)
-{
- ssk->time_wait = 0;
- sdp_set_state(&ssk->isk.sk, TCP_CLOSE);
- queue_work(sdp_workqueue, &ssk->destroy_work);
+ if (sdp_sk(sk)->id)
+ rdma_disconnect(sdp_sk(sk)->id);
}
static int sdp_init_sock(struct sock *sk)
INIT_LIST_HEAD(&ssk->accept_queue);
INIT_LIST_HEAD(&ssk->backlog_queue);
- INIT_DELAYED_WORK(&ssk->time_wait_work, sdp_time_wait_work);
+ INIT_DELAYED_WORK(&ssk->dreq_wait_work, sdp_dreq_wait_timeout_work);
INIT_WORK(&ssk->destroy_work, sdp_destroy_work);
sk->sk_route_caps |= NETIF_F_SG | NETIF_F_NO_CSUM;
+
+ ssk->sdp_disconnect = 0;
+ ssk->destructed_already = 0;
+ spin_lock_init(&ssk->lock);
+
return 0;
}
if (!(how & SEND_SHUTDOWN))
return;
- if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
- return;
-
- if (sk->sk_state == TCP_ESTABLISHED)
- sdp_set_state(sk, TCP_FIN_WAIT1);
- else if (sk->sk_state == TCP_CLOSE_WAIT)
- sdp_set_state(sk, TCP_LAST_ACK);
- else
- return;
+ if (!sdp_close_state(sk))
+ return;
/*
* Just turn off CORK here.
if (ssk->nonagle & TCP_NAGLE_OFF)
ssk->nonagle |= TCP_NAGLE_PUSH;
- sdp_post_sends(ssk, 0);
+ sdp_send_disconnect(sk);
}
static void sdp_mark_push(struct sdp_sock *ssk, struct sk_buff *skb)
sdp_warn(sk, "rdma_listen failed: %d\n", rc);
sdp_set_error(sk, rc);
} else
- sdp_set_state(sk, TCP_LISTEN);
+ sdp_exch_state(sk, TCPF_CLOSE, TCP_LISTEN);
return rc;
}