some of the supported socket options are sent to HW while rest are handled by SW Signed-off-by: Atul Gupta <atul.gupta@xxxxxxxxxxx> --- drivers/crypto/chelsio/chtls/chtls.h | 10 ++ drivers/crypto/chelsio/chtls/chtls_cm.h | 12 ++ drivers/crypto/chelsio/chtls/chtls_hw.c | 2 +- drivers/crypto/chelsio/chtls/chtls_main.c | 191 +++++++++++++++++++++++++++++- 4 files changed, 211 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/chelsio/chtls/chtls.h b/drivers/crypto/chelsio/chtls/chtls.h index a53a0e6..3e46d28 100644 --- a/drivers/crypto/chelsio/chtls/chtls.h +++ b/drivers/crypto/chelsio/chtls/chtls.h @@ -353,6 +353,15 @@ enum { #define TCP_PAGE(sk) (sk->sk_frag.page) #define TCP_OFF(sk) (sk->sk_frag.offset) +struct tcp_cong_ops { + struct tcp_congestion_ops ops; + int key; +}; + +#define CONG_OPS(__s, __k) \ + { { .name = __s, .owner = THIS_MODULE }, .key = CONG_ALG_##__k, } +#define CONG_ALG_NONE (-1) + static inline struct chtls_dev *to_chtls_dev(struct tls_device *tlsdev) { return container_of(tlsdev, struct chtls_dev, tlsdev); @@ -472,6 +481,7 @@ int send_tx_flowc_wr(struct sock *sk, int compl, void chtls_tcp_push(struct sock *sk, int flags); int chtls_push_frames(struct chtls_sock *csk, int comp); int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val); +int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val); int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 mode); void skb_entail(struct sock *sk, struct sk_buff *skb, int flags); unsigned int keyid_to_addr(int start_addr, int keyid); diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.h b/drivers/crypto/chelsio/chtls/chtls_cm.h index 78eb3af..569b723 100644 --- a/drivers/crypto/chelsio/chtls/chtls_cm.h +++ b/drivers/crypto/chelsio/chtls/chtls_cm.h @@ -36,9 +36,21 @@ #define TF_TLS_ENABLE_S 0 #define TF_TLS_ENABLE_V(x) ((x) << TF_TLS_ENABLE_S) +#define TF_NAGLE_S 7 +#define TF_NAGLE_V(x) ((x) << TF_NAGLE_S) + #define TF_RX_QUIESCE_S 15 #define TF_RX_QUIESCE_V(x) ((x) << TF_RX_QUIESCE_S) +#define TF_TURBO_S 21 +#define TF_TURBO_V(x) ((x) << TF_TURBO_S) + +#define TF_CCTRL_SEL0_S 22 +#define TF_CCTRL_SEL0_V(x) ((x) << TF_CCTRL_SEL0_S) + +#define TCB_TOS_S 10 +#define TCB_TOS_V(x) ((x) << TCB_TOS_S) + /* * Max receive window supported by HW in bytes. Only a small part of it can * be set through option0, the rest needs to be set through RX_DATA_ACK. diff --git a/drivers/crypto/chelsio/chtls/chtls_hw.c b/drivers/crypto/chelsio/chtls/chtls_hw.c index 55d5014..1b7ee6b 100644 --- a/drivers/crypto/chelsio/chtls/chtls_hw.c +++ b/drivers/crypto/chelsio/chtls/chtls_hw.c @@ -61,7 +61,7 @@ static void __set_tcb_field(struct sock *sk, struct sk_buff *skb, u16 word, * Send control message to HW, message go as immediate data and packet * is freed immediately. */ -static int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val) +int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val) { struct cpl_set_tcb_field *req; unsigned int credits_needed; diff --git a/drivers/crypto/chelsio/chtls/chtls_main.c b/drivers/crypto/chelsio/chtls/chtls_main.c index 4dc3d0e..9c3255d 100644 --- a/drivers/crypto/chelsio/chtls/chtls_main.c +++ b/drivers/crypto/chelsio/chtls/chtls_main.c @@ -512,15 +512,200 @@ static int do_chtls_setsockopt(struct sock *sk, int optname, return rc; } -static int chtls_setsockopt(struct sock *sk, int level, int optname, +void chtls_set_tos(struct sock *sk) +{ + u64 mask, val; + + mask = 0x3FULL; + val = (inet_sk(sk)->tos >> 2) & 0x3F; + chtls_set_tcb_field(sk, 3, TCB_TOS_V(mask), TCB_TOS_V(val)); +} + +#define UNSUP_IP_SOCK_OPT ((1 << IP_OPTIONS)) + +/* + * Socket option code for IP. + */ +static int do_ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { + if (level != SOL_IP) + return -ENOPROTOOPT; + + /* unsupported options */ + if ((1 << optname) & UNSUP_IP_SOCK_OPT) + return -ENOPROTOOPT; + + /* specially handled options */ + if (optname == IP_TOS) { + struct inet_sock *inet = inet_sk(sk); + int val = 0, err = 0; + + if (optlen >= sizeof(int)) { + if (get_user(val, (int __user *)optval)) + return -EFAULT; + } else if (optlen >= sizeof(char)) { + unsigned char ucval; + + if (get_user(ucval, (unsigned char __user *)optval)) + return -EFAULT; + val = (int)ucval; + } + lock_sock(sk); + val &= ~3; + val |= inet->tos & 3; + if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && + !capable(CAP_NET_ADMIN)) + err = -EPERM; + else if (inet->tos != val) { + inet->tos = val; + sk->sk_priority = rt_tos2priority(val); + chtls_set_tos(sk); + } + release_sock(sk); + return err; + } + return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, optname, + optval, optlen); +} + +static struct tcp_cong_ops cong_ops[] = { + CONG_OPS("reno", RENO), + CONG_OPS("tahoe", TAHOE), + CONG_OPS("newreno", NEWRENO), + CONG_OPS("highspeed", HIGHSPEED), + CONG_OPS("none", NONE), +}; + +int tcp_set_cong_control(struct sock *sk, const char *name) +{ + int cong_algo; + u64 mask, val; + + for (cong_algo = 0; cong_algo < ARRAY_SIZE(cong_ops); cong_algo++) + if (!strcmp(name, cong_ops[cong_algo].ops.name)) + break; + + if (cong_algo >= ARRAY_SIZE(cong_ops)) + return -EINVAL; + + mask = TF_TURBO_V(1) | TF_CCTRL_SEL0_V(3); + if (cong_ops[cong_algo].key == CONG_ALG_NONE) + val = TF_TURBO_V(1); + else + val = TF_CCTRL_SEL0_V(cong_ops[cong_algo].key); + + chtls_set_tcb_field(sk, 1, mask, val); + return 0; +} + +static void chtls_nagle(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + int val; + + val = !(tp->nonagle & TCP_NAGLE_OFF); + chtls_set_tcb_field(sk, 1, TF_NAGLE_V(1), TF_NAGLE_V(val)); +} + +static void chtls_uncork(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (tp->nonagle & TCP_NAGLE_CORK) { + tp->nonagle &= ~TCP_NAGLE_CORK; + chtls_tcp_push(sk, 0); + } +} + +/* + * Socket option code for IP. + */ +static int do_tcp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ struct tls_context *ctx = tls_get_ctx(sk); + struct tcp_sock *tp = tcp_sk(sk); + int val, err = 0; + + switch (optname) { + case TCP_CONGESTION: { + char name[TCP_CA_NAME_MAX]; + + if (optlen < 1) + return -EINVAL; + + val = strncpy_from_user(name, optval, + min_t(long, TCP_CA_NAME_MAX - 1, + optlen)); + if (val < 0) + return -EFAULT; + name[val] = 0; + return tcp_set_cong_control(sk, name); + } + default: + break; + } - if (level != SOL_TLS) + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + lock_sock(sk); + switch (optname) { + case TCP_NODELAY: { + int oldval = tp->nonagle; + + if (val) + tp->nonagle |= TCP_NAGLE_OFF; + else + tp->nonagle &= ~TCP_NAGLE_OFF; + + if (oldval != tp->nonagle) + chtls_nagle(sk); + break; + } + + case TCP_CORK: + if (val) + tp->nonagle |= TCP_NAGLE_CORK; + else + chtls_uncork(sk); + break; + + case TCP_KEEPIDLE: + if (val < 1 || val > MAX_TCP_KEEPIDLE) + err = -EINVAL; + else + tp->keepalive_time = val * HZ; + break; + + case TCP_QUICKACK: + if (!val) + inet_csk(sk)->icsk_ack.pingpong = 1; + else + inet_csk(sk)->icsk_ack.pingpong = 0; + break; + + default: + release_sock(sk); return ctx->setsockopt(sk, level, optname, optval, optlen); + } + release_sock(sk); + return err; +} + +static int chtls_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen) +{ + if (level == SOL_TLS) + return do_chtls_setsockopt(sk, optname, optval, optlen); - return do_chtls_setsockopt(sk, optname, optval, optlen); + return level != SOL_TCP ? + do_ip_setsockopt(sk, level, optname, optval, optlen) : + do_tcp_setsockopt(sk, level, optname, optval, optlen); } static struct cxgb4_uld_info chtls_uld_info = { -- 1.8.3.1