Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b38d7324 authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller
Browse files

smc: socket closing and linkgroup cleanup



smc_shutdown() and smc_release() handling
delayed linkgroup cleanup for linkgroups without connections

Signed-off-by: default avatarUrsula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 952310cc
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-$(CONFIG_SMC)	+= smc.o
smc-y := af_smc.o smc_pnet.o smc_ib.o smc_clc.o smc_core.o smc_wr.o smc_llc.o
smc-y += smc_cdc.o smc_tx.o smc_rx.o
smc-y += smc_cdc.o smc_tx.o smc_rx.o smc_close.o
+105 −22
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include "smc_pnet.h"
#include "smc_tx.h"
#include "smc_rx.h"
#include "smc_close.h"

static DEFINE_MUTEX(smc_create_lgr_pending);	/* serialize link group
						 * creation
@@ -70,14 +71,29 @@ static int smc_release(struct socket *sock)
{
	struct sock *sk = sock->sk;
	struct smc_sock *smc;
	int rc = 0;

	if (!sk)
		goto out;

	smc = smc_sk(sk);
	sock_hold(sk);
	if (sk->sk_state == SMC_LISTEN)
		/* smc_close_non_accepted() is called and acquires
		 * sock lock for child sockets again
		 */
		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
	else
		lock_sock(sk);

	if (smc->use_fallback) {
		sk->sk_state = SMC_CLOSED;
		sk->sk_state_change(sk);
	} else {
		rc = smc_close_active(smc);
		sock_set_flag(sk, SOCK_DEAD);
		sk->sk_shutdown |= SHUTDOWN_MASK;
	}
	if (smc->clcsock) {
		sock_release(smc->clcsock);
		smc->clcsock = NULL;
@@ -86,11 +102,18 @@ static int smc_release(struct socket *sock)
	/* detach socket */
	sock_orphan(sk);
	sock->sk = NULL;
	if (smc->use_fallback) {
		schedule_delayed_work(&smc->sock_put_work, TCP_TIMEWAIT_LEN);
	} else if (sk->sk_state == SMC_CLOSED) {
		smc_conn_free(&smc->conn);
		schedule_delayed_work(&smc->sock_put_work,
				      SMC_CLOSE_SOCK_PUT_DELAY);
	}
	release_sock(sk);

	sock_put(sk);
out:
	return 0;
	return rc;
}

static void smc_destruct(struct sock *sk)
@@ -120,6 +143,7 @@ static struct sock *smc_sock_alloc(struct net *net, struct socket *sock)
	INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
	INIT_LIST_HEAD(&smc->accept_q);
	spin_lock_init(&smc->accept_q_lock);
	INIT_DELAYED_WORK(&smc->sock_put_work, smc_close_sock_put_work);
	sk_refcnt_debug_inc(sk);

	return sk;
@@ -417,6 +441,7 @@ static int smc_connect_rdma(struct smc_sock *smc)

out_connected:
	smc_copy_sock_settings_to_clc(smc);
	if (smc->sk.sk_state == SMC_INIT)
		smc->sk.sk_state = SMC_ACTIVE;

	return rc ? rc : local_contact;
@@ -559,7 +584,7 @@ static void smc_accept_unlink(struct sock *sk)
/* remove a sock from the accept queue to bind it to a new socket created
 * for a socket accept call from user space
 */
static struct sock *smc_accept_dequeue(struct sock *parent,
struct sock *smc_accept_dequeue(struct sock *parent,
				struct socket *new_sock)
{
	struct smc_sock *isk, *n;
@@ -581,11 +606,17 @@ static struct sock *smc_accept_dequeue(struct sock *parent,
}

/* clean up for a created but never accepted sock */
static void smc_close_non_accepted(struct sock *sk)
void smc_close_non_accepted(struct sock *sk)
{
	struct smc_sock *smc = smc_sk(sk);

	sock_hold(sk);
	lock_sock(sk);
	if (!sk->sk_lingertime)
		/* wait for peer closing */
		sk->sk_lingertime = SMC_MAX_STREAM_WAIT_TIMEOUT;
	if (!smc->use_fallback)
		smc_close_active(smc);
	if (smc->clcsock) {
		struct socket *tcp;

@@ -593,7 +624,16 @@ static void smc_close_non_accepted(struct sock *sk)
		smc->clcsock = NULL;
		sock_release(tcp);
	}
	/* more closing stuff to be added with socket closing patch */
	sock_set_flag(sk, SOCK_DEAD);
	sk->sk_shutdown |= SHUTDOWN_MASK;
	if (smc->use_fallback) {
		schedule_delayed_work(&smc->sock_put_work, TCP_TIMEWAIT_LEN);
	} else {
		smc_conn_free(&smc->conn);
		schedule_delayed_work(&smc->sock_put_work,
				      SMC_CLOSE_SOCK_PUT_DELAY);
	}
	release_sock(sk);
	sock_put(sk);
}

@@ -761,11 +801,12 @@ static void smc_listen_work(struct work_struct *work)

out_connected:
	sk_refcnt_debug_inc(newsmcsk);
	if (newsmcsk->sk_state == SMC_INIT)
		newsmcsk->sk_state = SMC_ACTIVE;
enqueue:
	if (local_contact == SMC_FIRST_CONTACT)
		mutex_unlock(&smc_create_lgr_pending);
	lock_sock(&lsmc->sk);
	lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
	if (lsmc->sk.sk_state == SMC_LISTEN) {
		smc_accept_enqueue(&lsmc->sk, newsmcsk);
	} else { /* no longer listening */
@@ -791,6 +832,7 @@ static void smc_listen_work(struct work_struct *work)

out_err:
	newsmcsk->sk_state = SMC_CLOSED;
	smc_conn_free(&new_smc->conn);
	goto enqueue; /* queue new sock with sk_err set */
}

@@ -911,7 +953,8 @@ static int smc_getname(struct socket *sock, struct sockaddr *addr,
{
	struct smc_sock *smc;

	if (peer && (sock->sk->sk_state != SMC_ACTIVE))
	if (peer && (sock->sk->sk_state != SMC_ACTIVE) &&
	    (sock->sk->sk_state != SMC_APPCLOSEWAIT1))
		return -ENOTCONN;

	smc = smc_sk(sock->sk);
@@ -927,7 +970,9 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)

	smc = smc_sk(sk);
	lock_sock(sk);
	if (sk->sk_state != SMC_ACTIVE)
	if ((sk->sk_state != SMC_ACTIVE) &&
	    (sk->sk_state != SMC_APPCLOSEWAIT1) &&
	    (sk->sk_state != SMC_INIT))
		goto out;
	if (smc->use_fallback)
		rc = smc->clcsock->ops->sendmsg(smc->clcsock, msg, len);
@@ -947,13 +992,21 @@ static int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,

	smc = smc_sk(sk);
	lock_sock(sk);
	if ((sk->sk_state != SMC_ACTIVE) && (sk->sk_state != SMC_CLOSED))
	if ((sk->sk_state == SMC_INIT) ||
	    (sk->sk_state == SMC_LISTEN) ||
	    (sk->sk_state == SMC_CLOSED))
		goto out;

	if (sk->sk_state == SMC_PEERFINCLOSEWAIT) {
		rc = 0;
		goto out;
	}

	if (smc->use_fallback)
		rc = smc->clcsock->ops->recvmsg(smc->clcsock, msg, len, flags);
	else
		rc = smc_rx_recvmsg(smc, msg, len, flags);

out:
	release_sock(sk);
	return rc;
@@ -1013,7 +1066,8 @@ static unsigned int smc_poll(struct file *file, struct socket *sock,
			mask |= smc_accept_poll(sk);
		if (sk->sk_err)
			mask |= POLLERR;
		if (atomic_read(&smc->conn.sndbuf_space)) {
		if (atomic_read(&smc->conn.sndbuf_space) ||
		    (sk->sk_shutdown & SEND_SHUTDOWN)) {
			mask |= POLLOUT | POLLWRNORM;
		} else {
			sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
@@ -1021,7 +1075,14 @@ static unsigned int smc_poll(struct file *file, struct socket *sock,
		}
		if (atomic_read(&smc->conn.bytes_to_rcv))
			mask |= POLLIN | POLLRDNORM;
		/* for now - to be enhanced in follow-on patch */
		if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
		    (sk->sk_state == SMC_CLOSED))
			mask |= POLLHUP;
		if (sk->sk_shutdown & RCV_SHUTDOWN)
			mask |= POLLIN | POLLRDNORM | POLLRDHUP;
		if (sk->sk_state == SMC_APPCLOSEWAIT1)
			mask |= POLLIN;

	}

	return mask;
@@ -1032,31 +1093,53 @@ static int smc_shutdown(struct socket *sock, int how)
	struct sock *sk = sock->sk;
	struct smc_sock *smc;
	int rc = -EINVAL;
	int rc1 = 0;

	smc = smc_sk(sk);

	if ((how < SHUT_RD) || (how > SHUT_RDWR))
		goto out_err;
		return rc;

	lock_sock(sk);

	rc = -ENOTCONN;
	if (sk->sk_state == SMC_CLOSED)
	if ((sk->sk_state != SMC_LISTEN) &&
	    (sk->sk_state != SMC_ACTIVE) &&
	    (sk->sk_state != SMC_PEERCLOSEWAIT1) &&
	    (sk->sk_state != SMC_PEERCLOSEWAIT2) &&
	    (sk->sk_state != SMC_APPCLOSEWAIT1) &&
	    (sk->sk_state != SMC_APPCLOSEWAIT2) &&
	    (sk->sk_state != SMC_APPFINCLOSEWAIT))
		goto out;
	if (smc->use_fallback) {
		rc = kernel_sock_shutdown(smc->clcsock, how);
		sk->sk_shutdown = smc->clcsock->sk->sk_shutdown;
		if (sk->sk_shutdown == SHUTDOWN_MASK)
			sk->sk_state = SMC_CLOSED;
	} else {
		rc = sock_no_shutdown(sock, how);
		goto out;
	}
	switch (how) {
	case SHUT_RDWR:		/* shutdown in both directions */
		rc = smc_close_active(smc);
		break;
	case SHUT_WR:
		rc = smc_close_shutdown_write(smc);
		break;
	case SHUT_RD:
		if (sk->sk_state == SMC_LISTEN)
			rc = smc_close_active(smc);
		else
			rc = 0;
			/* nothing more to do because peer is not involved */
		break;
	}
	rc1 = kernel_sock_shutdown(smc->clcsock, how);
	/* map sock_shutdown_cmd constants to sk_shutdown value range */
	sk->sk_shutdown |= how + 1;

out:
	release_sock(sk);

out_err:
	return rc;
	return rc ? rc : rc1;
}

static int smc_setsockopt(struct socket *sock, int level, int optname,
+18 −0
Original line number Diff line number Diff line
@@ -30,6 +30,16 @@ enum smc_state { /* possible states of an SMC socket */
	SMC_INIT	= 2,
	SMC_CLOSED	= 7,
	SMC_LISTEN	= 10,
	/* normal close */
	SMC_PEERCLOSEWAIT1	= 20,
	SMC_PEERCLOSEWAIT2	= 21,
	SMC_APPFINCLOSEWAIT	= 24,
	SMC_APPCLOSEWAIT1	= 22,
	SMC_APPCLOSEWAIT2	= 23,
	SMC_PEERFINCLOSEWAIT	= 25,
	/* abnormal close */
	SMC_PEERABORTWAIT	= 26,
	SMC_PROCESSABORT	= 27,
};

struct smc_link_group;
@@ -164,7 +174,13 @@ struct smc_sock { /* smc sock container */
	struct work_struct	smc_listen_work;/* prepare new accept socket */
	struct list_head	accept_q;	/* sockets to be accepted */
	spinlock_t		accept_q_lock;	/* protects accept_q */
	struct delayed_work	sock_put_work;	/* final socket freeing */
	bool			use_fallback;	/* fallback to tcp */
	u8			wait_close_tx_prepared : 1;
						/* shutdown wr or close
						 * started, waiting for unsent
						 * data to be sent
						 */
};

static inline struct smc_sock *smc_sk(const struct sock *sk)
@@ -250,5 +266,7 @@ void smc_conn_free(struct smc_connection *conn);
int smc_conn_create(struct smc_sock *smc, __be32 peer_in_addr,
		    struct smc_ib_device *smcibdev, u8 ibport,
		    struct smc_clc_msg_local *lcl, int srv_first_contact);
struct sock *smc_accept_dequeue(struct sock *parent, struct socket *new_sock);
void smc_close_non_accepted(struct sock *sk);

#endif	/* __SMC_H */
+20 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include "smc_cdc.h"
#include "smc_tx.h"
#include "smc_rx.h"
#include "smc_close.h"

/********************************** send *************************************/

@@ -55,6 +56,9 @@ static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd,
			       cdcpend->conn);
	}
	smc_tx_sndbuf_nonfull(smc);
	if (smc->sk.sk_state != SMC_ACTIVE)
		/* wake up smc_close_wait_tx_pends() */
		smc->sk.sk_state_change(&smc->sk);
	bh_unlock_sock(&smc->sk);
}

@@ -149,6 +153,14 @@ void smc_cdc_tx_dismiss_slots(struct smc_connection *conn)
				(unsigned long)conn);
}

bool smc_cdc_tx_has_pending(struct smc_connection *conn)
{
	struct smc_link *link = &conn->lgr->lnk[SMC_SINGLE_LINK];

	return smc_wr_tx_has_pending(link, SMC_CDC_MSG_TYPE,
				     smc_cdc_tx_filter, (unsigned long)conn);
}

/********************************* receive ***********************************/

static inline bool smc_cdc_before(u16 seq1, u16 seq2)
@@ -201,15 +213,20 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
		smc->sk.sk_data_ready(&smc->sk);
	}

	if (conn->local_rx_ctrl.conn_state_flags.peer_conn_abort)
	if (conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) {
		smc->sk.sk_err = ECONNRESET;
		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
	}
	if (smc_cdc_rxed_any_close_or_senddone(conn))
		/* subsequent patch: terminate connection */
		smc_close_passive_received(smc);

	/* piggy backed tx info */
	/* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */
	if (diff_cons && smc_tx_prepared_sends(conn))
	if (diff_cons && smc_tx_prepared_sends(conn)) {
		smc_tx_sndbuf_nonempty(conn);
		/* trigger socket release if connection closed */
		smc_close_wake_tx_prepared(smc);
	}

	/* subsequent patch: trigger socket release if connection closed */

+1 −0
Original line number Diff line number Diff line
@@ -212,6 +212,7 @@ void smc_cdc_tx_dismiss_slots(struct smc_connection *conn);
int smc_cdc_msg_send(struct smc_connection *conn, struct smc_wr_buf *wr_buf,
		     struct smc_cdc_tx_pend *pend);
int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn);
bool smc_cdc_tx_has_pending(struct smc_connection *conn);
int smc_cdc_init(void) __init;

#endif /* SMC_CDC_H */
Loading