HW supported socket options are handled by HW while rest
are handled by SW

Signed-off-by: Atul Gupta <atul.gu...@chelsio.com>
---
 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 1ef56d6..7d6965e 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

Reply via email to