return 0;
 }
 
+union tls_iter_offset {
+       struct iov_iter *msg_iter;
+       int offset;
+};
+
 static int tls_push_data(struct sock *sk,
-                        struct iov_iter *msg_iter,
+                        union tls_iter_offset iter_offset,
                         size_t size, int flags,
-                        unsigned char record_type)
+                        unsigned char record_type,
+                        struct page *zc_page)
 {
        struct tls_context *tls_ctx = tls_get_ctx(sk);
        struct tls_prot_info *prot = &tls_ctx->prot_info;
                }
 
                record = ctx->open_record;
-               copy = min_t(size_t, size, (pfrag->size - pfrag->offset));
-               copy = min_t(size_t, copy, (max_open_record_len - record->len));
 
-               if (copy) {
+               copy = min_t(size_t, size, max_open_record_len - record->len);
+               if (copy && zc_page) {
+                       struct page_frag zc_pfrag;
+
+                       zc_pfrag.page = zc_page;
+                       zc_pfrag.offset = iter_offset.offset;
+                       zc_pfrag.size = copy;
+                       tls_append_frag(record, &zc_pfrag, copy);
+               } else if (copy) {
+                       copy = min_t(size_t, copy, pfrag->size - pfrag->offset);
+
                        rc = tls_device_copy_data(page_address(pfrag->page) +
-                                                 pfrag->offset, copy, msg_iter);
+                                                 pfrag->offset, copy,
+                                                 iter_offset.msg_iter);
                        if (rc)
                                goto handle_error;
                        tls_append_frag(record, pfrag, copy);
 {
        unsigned char record_type = TLS_RECORD_TYPE_DATA;
        struct tls_context *tls_ctx = tls_get_ctx(sk);
+       union tls_iter_offset iter;
        int rc;
 
        mutex_lock(&tls_ctx->tx_lock);
                        goto out;
        }
 
-       rc = tls_push_data(sk, &msg->msg_iter, size,
-                          msg->msg_flags, record_type);
+       iter.msg_iter = &msg->msg_iter;
+       rc = tls_push_data(sk, iter, size, msg->msg_flags, record_type, NULL);
 
 out:
        release_sock(sk);
                        int offset, size_t size, int flags)
 {
        struct tls_context *tls_ctx = tls_get_ctx(sk);
-       struct iov_iter msg_iter;
+       union tls_iter_offset iter_offset;
+       struct iov_iter msg_iter;
        char *kaddr;
        struct kvec iov;
        int rc;
                goto out;
        }
 
+       if (tls_ctx->zerocopy_sendfile) {
+               iter_offset.offset = offset;
+               rc = tls_push_data(sk, iter_offset, size,
+                                  flags, TLS_RECORD_TYPE_DATA, page);
+               goto out;
+       }
+
        kaddr = kmap(page);
        iov.iov_base = kaddr + offset;
        iov.iov_len = size;
        iov_iter_kvec(&msg_iter, WRITE, &iov, 1, size);
-       rc = tls_push_data(sk, &msg_iter, size,
-                          flags, TLS_RECORD_TYPE_DATA);
+       iter_offset.msg_iter = &msg_iter;
+       rc = tls_push_data(sk, iter_offset, size, flags, TLS_RECORD_TYPE_DATA,
+                          NULL);
        kunmap(page);
 
 out:
 
 static int tls_device_push_pending_record(struct sock *sk, int flags)
 {
-       struct iov_iter msg_iter;
+       union tls_iter_offset iter;
+       struct iov_iter msg_iter;
 
        iov_iter_kvec(&msg_iter, WRITE, NULL, 0, 0);
-       return tls_push_data(sk, &msg_iter, 0, flags, TLS_RECORD_TYPE_DATA);
+       iter.msg_iter = &msg_iter;
+       return tls_push_data(sk, iter, 0, flags, TLS_RECORD_TYPE_DATA, NULL);
 }
 
 void tls_device_write_space(struct sock *sk, struct tls_context *ctx)
 
        return rc;
 }
 
+static int do_tls_getsockopt_tx_zc(struct sock *sk, char __user *optval,
+                                  int __user *optlen)
+{
+       struct tls_context *ctx = tls_get_ctx(sk);
+       unsigned int value;
+       int len;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       if (len != sizeof(value))
+               return -EINVAL;
+
+       value = ctx->zerocopy_sendfile;
+       if (copy_to_user(optval, &value, sizeof(value)))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int do_tls_getsockopt(struct sock *sk, int optname,
                             char __user *optval, int __user *optlen)
 {
                rc = do_tls_getsockopt_conf(sk, optval, optlen,
                                            optname == TLS_TX);
                break;
+       case TLS_TX_ZEROCOPY_SENDFILE:
+               rc = do_tls_getsockopt_tx_zc(sk, optval, optlen);
+               break;
        default:
                rc = -ENOPROTOOPT;
                break;
        return rc;
 }
 
+static int do_tls_setsockopt_tx_zc(struct sock *sk, sockptr_t optval,
+                                  unsigned int optlen)
+{
+       struct tls_context *ctx = tls_get_ctx(sk);
+       unsigned int value;
+
+       if (sockptr_is_null(optval) || optlen != sizeof(value))
+               return -EINVAL;
+
+       if (copy_from_sockptr(&value, optval, sizeof(value)))
+               return -EFAULT;
+
+       if (value > 1)
+               return -EINVAL;
+
+       ctx->zerocopy_sendfile = value;
+
+       return 0;
+}
+
 static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval,
                             unsigned int optlen)
 {
                                            optname == TLS_TX);
                release_sock(sk);
                break;
+       case TLS_TX_ZEROCOPY_SENDFILE:
+               lock_sock(sk);
+               rc = do_tls_setsockopt_tx_zc(sk, optval, optlen);
+               release_sock(sk);
+               break;
        default:
                rc = -ENOPROTOOPT;
                break;
        if (err)
                goto nla_failure;
 
+       if (ctx->tx_conf == TLS_HW && ctx->zerocopy_sendfile) {
+               err = nla_put_flag(skb, TLS_INFO_ZC_SENDFILE);
+               if (err)
+                       goto nla_failure;
+       }
+
        rcu_read_unlock();
        nla_nest_end(skb, start);
        return 0;
                nla_total_size(sizeof(u16)) +   /* TLS_INFO_CIPHER */
                nla_total_size(sizeof(u16)) +   /* TLS_INFO_RXCONF */
                nla_total_size(sizeof(u16)) +   /* TLS_INFO_TXCONF */
+               nla_total_size(0) +             /* TLS_INFO_ZC_SENDFILE */
                0;
 
        return size;