@@ -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);
@@ -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.
@@ -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;
@@ -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 = {
some of the supported socket options are sent to HW while rest are handled by SW Signed-off-by: Atul Gupta <atul.gupta@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(-)