@@ -3038,58 +3038,40 @@ static int __smc_setsockopt(struct socket *sock, int level, int optname,
return rc;
}
-static int smc_setsockopt(struct socket *sock, int level, int optname,
- sockptr_t optval, unsigned int optlen)
+/* When an unsupported sockopt is found,
+ * SMC should try it best to fallback. If fallback is not possible,
+ * an error should be explicitly returned.
+ */
+static inline bool smc_is_unsupport_tcp_sockopt(int optname)
+{
+ switch (optname) {
+ case TCP_FASTOPEN:
+ case TCP_FASTOPEN_CONNECT:
+ case TCP_FASTOPEN_KEY:
+ case TCP_FASTOPEN_NO_COOKIE:
+ return true;
+ }
+ return false;
+}
+
+static int smc_setsockopt_common(struct socket *sock, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct smc_sock *smc;
- int val, rc;
-
- if (level == SOL_TCP && optname == TCP_ULP)
- return -EOPNOTSUPP;
- else if (level == SOL_SMC)
- return __smc_setsockopt(sock, level, optname, optval, optlen);
+ int val, rc = 0;
smc = smc_sk(sk);
- /* generic setsockopts reaching us here always apply to the
- * CLC socket
- */
- mutex_lock(&smc->clcsock_release_lock);
- if (!smc->clcsock) {
- mutex_unlock(&smc->clcsock_release_lock);
- return -EBADF;
- }
- if (unlikely(!smc->clcsock->ops->setsockopt))
- rc = -EOPNOTSUPP;
- else
- rc = smc->clcsock->ops->setsockopt(smc->clcsock, level, optname,
- optval, optlen);
- if (smc->clcsock->sk->sk_err) {
- sk->sk_err = smc->clcsock->sk->sk_err;
- sk_error_report(sk);
- }
- mutex_unlock(&smc->clcsock_release_lock);
-
if (optlen < sizeof(int))
return -EINVAL;
if (copy_from_sockptr(&val, optval, sizeof(int)))
return -EFAULT;
lock_sock(sk);
- if (rc || smc->use_fallback)
+ if (smc->use_fallback)
goto out;
switch (optname) {
- case TCP_FASTOPEN:
- case TCP_FASTOPEN_CONNECT:
- case TCP_FASTOPEN_KEY:
- case TCP_FASTOPEN_NO_COOKIE:
- /* option not supported by SMC */
- if (smc_sk_state(sk) == SMC_INIT && !smc->connect_nonblock)
- rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
- else
- rc = -EINVAL;
- break;
case TCP_NODELAY:
if (smc_sk_state(sk) != SMC_INIT &&
smc_sk_state(sk) != SMC_LISTEN &&
@@ -3116,6 +3098,13 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
smc->sockopt_defer_accept = val;
break;
default:
+ if (smc_is_unsupport_tcp_sockopt(optname)) {
+ /* option not supported by SMC */
+ if (smc_sk_state(sk) == SMC_INIT && !smc->connect_nonblock)
+ rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
+ else
+ rc = -EINVAL;
+ }
break;
}
out:
@@ -3124,6 +3113,42 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
return rc;
}
+static int smc_setsockopt(struct socket *sock, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct smc_sock *smc;
+ int rc;
+
+ if (level == SOL_TCP && optname == TCP_ULP)
+ return -EOPNOTSUPP;
+ else if (level == SOL_SMC)
+ return __smc_setsockopt(sock, level, optname, optval, optlen);
+
+ smc = smc_sk(sk);
+
+ /* generic setsockopts reaching us here always apply to the
+ * CLC socket
+ */
+ mutex_lock(&smc->clcsock_release_lock);
+ if (!smc->clcsock) {
+ mutex_unlock(&smc->clcsock_release_lock);
+ return -EBADF;
+ }
+ if (unlikely(!smc->clcsock->ops->setsockopt))
+ rc = -EOPNOTSUPP;
+ else
+ rc = smc->clcsock->ops->setsockopt(smc->clcsock, level, optname,
+ optval, optlen);
+ if (smc->clcsock->sk->sk_err) {
+ sk->sk_err = smc->clcsock->sk->sk_err;
+ sk_error_report(sk);
+ }
+ mutex_unlock(&smc->clcsock_release_lock);
+
+ return rc ?: smc_setsockopt_common(sock, level, optname, optval, optlen);
+}
+
static int smc_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{