Commit e2b76ab8 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French
Browse files

ksmbd: add support for read compound



MacOS sends a compound request including read to the server
(e.g. open-read-close). So far, ksmbd has not handled read as
a compound request. For compatibility between ksmbd and an OS that
supports SMB, This patch provides compound support for read requests.

Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 084ba46f
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -1029,11 +1029,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
{
	struct scatterlist *sg;
	unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
	int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0;
	int i, *nr_entries, total_entries = 0, sg_idx = 0;

	if (!nvec)
		return NULL;

	nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
	if (!nr_entries)
		return NULL;

	for (i = 0; i < nvec - 1; i++) {
		unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;

@@ -1051,8 +1055,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
	total_entries += 2;

	sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
	if (!sg)
	if (!sg) {
		kfree(nr_entries);
		return NULL;
	}

	sg_init_table(sg, total_entries);
	smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len);
@@ -1086,6 +1092,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
		}
	}
	smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
	kfree(nr_entries);
	return sg;
}

+15 −40
Original line number Diff line number Diff line
@@ -123,28 +123,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
	}
}

int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
{
	struct ksmbd_conn *conn = work->conn;
	int ret = 1;

	if (list_empty(&work->request_entry) &&
	    list_empty(&work->async_request_entry))
		return 0;
		return;

	if (!work->multiRsp)
	atomic_dec(&conn->req_running);
	if (!work->multiRsp) {
	spin_lock(&conn->request_lock);
	list_del_init(&work->request_entry);
	spin_unlock(&conn->request_lock);
	if (work->asynchronous)
		release_async_work(work);
		ret = 0;
	}

	wake_up_all(&conn->req_running_q);
	return ret;
}

void ksmbd_conn_lock(struct ksmbd_conn *conn)
@@ -193,39 +187,20 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
int ksmbd_conn_write(struct ksmbd_work *work)
{
	struct ksmbd_conn *conn = work->conn;
	size_t len = 0;
	int sent;
	struct kvec iov[3];
	int iov_idx = 0;

	if (!work->response_buf) {
		pr_err("NULL response header\n");
		return -EINVAL;
	}

	if (work->tr_buf) {
		iov[iov_idx] = (struct kvec) { work->tr_buf,
				sizeof(struct smb2_transform_hdr) + 4 };
		len += iov[iov_idx++].iov_len;
	}

	if (work->aux_payload_sz) {
		iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz };
		len += iov[iov_idx++].iov_len;
		iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz };
		len += iov[iov_idx++].iov_len;
	} else {
		if (work->tr_buf)
			iov[iov_idx].iov_len = work->resp_hdr_sz;
		else
			iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4;
		iov[iov_idx].iov_base = work->response_buf;
		len += iov[iov_idx++].iov_len;
	}
	if (work->send_no_response)
		return 0;

	ksmbd_conn_lock(conn);
	sent = conn->transport->ops->writev(conn->transport, &iov[0],
					iov_idx, len,
	sent = conn->transport->ops->writev(conn->transport, work->iov,
			work->iov_cnt,
			get_rfc1002_len(work->iov[0].iov_base) + 4,
			work->need_invalidate_rkey,
			work->remote_key);
	ksmbd_conn_unlock(conn);
+1 −1
Original line number Diff line number Diff line
@@ -158,7 +158,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
			  struct smb2_buffer_desc_v1 *desc,
			  unsigned int desc_len);
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
int ksmbd_conn_handler_loop(void *p);
int ksmbd_conn_transport_init(void);
+90 −1
Original line number Diff line number Diff line
@@ -27,18 +27,35 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
		INIT_LIST_HEAD(&work->async_request_entry);
		INIT_LIST_HEAD(&work->fp_entry);
		INIT_LIST_HEAD(&work->interim_entry);
		INIT_LIST_HEAD(&work->aux_read_list);
		work->iov_alloc_cnt = 4;
		work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
				    GFP_KERNEL);
		if (!work->iov) {
			kmem_cache_free(work_cache, work);
			work = NULL;
		}
	}
	return work;
}

void ksmbd_free_work_struct(struct ksmbd_work *work)
{
	struct aux_read *ar, *tmp;

	WARN_ON(work->saved_cred != NULL);

	kvfree(work->response_buf);
	kvfree(work->aux_payload_buf);

	list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {
		kvfree(ar->buf);
		list_del(&ar->entry);
		kfree(ar);
	}

	kfree(work->tr_buf);
	kvfree(work->request_buf);
	kfree(work->iov);
	if (work->async_id)
		ksmbd_release_id(&work->conn->async_ida, work->async_id);
	kmem_cache_free(work_cache, work);
@@ -77,3 +94,75 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
{
	return queue_work(ksmbd_wq, &work->work);
}

static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
				 unsigned int ib_len)
{

	if (work->iov_alloc_cnt <= work->iov_cnt) {
		struct kvec *new;

		work->iov_alloc_cnt += 4;
		new = krealloc(work->iov,
			       sizeof(struct kvec) * work->iov_alloc_cnt,
			       GFP_KERNEL | __GFP_ZERO);
		if (!new)
			return -ENOMEM;
		work->iov = new;
	}

	work->iov[++work->iov_idx].iov_base = ib;
	work->iov[work->iov_idx].iov_len = ib_len;
	work->iov_cnt++;

	return 0;
}

static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
			       void *aux_buf, unsigned int aux_size)
{
	/* Plus rfc_length size on first iov */
	if (!work->iov_idx) {
		work->iov[work->iov_idx].iov_base = work->response_buf;
		*(__be32 *)work->iov[0].iov_base = 0;
		work->iov[work->iov_idx].iov_len = 4;
		work->iov_cnt++;
	}

	ksmbd_realloc_iov_pin(work, ib, len);
	inc_rfc1001_len(work->iov[0].iov_base, len);

	if (aux_size) {
		struct aux_read *ar;

		ksmbd_realloc_iov_pin(work, aux_buf, aux_size);
		inc_rfc1001_len(work->iov[0].iov_base, aux_size);

		ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
		if (!ar)
			return -ENOMEM;

		ar->buf = aux_buf;
		list_add(&ar->entry, &work->aux_read_list);
	}

	return 0;
}

int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)
{
	return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);
}

int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
			   void *aux_buf, unsigned int aux_size)
{
	return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
}

void ksmbd_iov_reset(struct ksmbd_work *work)
{
	work->iov_idx = 0;
	work->iov_cnt = 0;
	*(__be32 *)work->iov[0].iov_base = 0;
}
+26 −8
Original line number Diff line number Diff line
@@ -19,6 +19,11 @@ enum {
	KSMBD_WORK_CLOSED,
};

struct aux_read {
	void *buf;
	struct list_head entry;
};

/* one of these for every pending CIFS request at the connection */
struct ksmbd_work {
	/* Server corresponding to this mid */
@@ -31,13 +36,19 @@ struct ksmbd_work {
	/* Response buffer */
	void                            *response_buf;

	/* Read data buffer */
	void                            *aux_payload_buf;
	struct list_head		aux_read_list;

	struct kvec			*iov;
	int				iov_alloc_cnt;
	int				iov_cnt;
	int				iov_idx;

	/* Next cmd hdr in compound req buf*/
	int                             next_smb2_rcv_hdr_off;
	/* Next cmd hdr in compound rsp buf*/
	int                             next_smb2_rsp_hdr_off;
	/* Current cmd hdr in compound rsp buf*/
	int                             curr_smb2_rsp_hdr_off;

	/*
	 * Current Local FID assigned compound response if SMB2 CREATE
@@ -53,16 +64,11 @@ struct ksmbd_work {
	unsigned int			credits_granted;

	/* response smb header size */
	unsigned int                    resp_hdr_sz;
	unsigned int                    response_sz;
	/* Read data count */
	unsigned int                    aux_payload_sz;

	void				*tr_buf;

	unsigned char			state;
	/* Multiple responses for one request e.g. SMB ECHO */
	bool                            multiRsp:1;
	/* No response for cancelled request */
	bool                            send_no_response:1;
	/* Request is encrypted */
@@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work)
	return work->response_buf + work->next_smb2_rsp_hdr_off + 4;
}

/**
 * ksmbd_resp_buf_curr - Get current buffer on compound response.
 * @work: smb work containing response buffer
 */
static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work)
{
	return work->response_buf + work->curr_smb2_rsp_hdr_off + 4;
}

/**
 * ksmbd_req_buf_next - Get next buffer on compound request.
 * @work: smb work containing response buffer
@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void);
int ksmbd_workqueue_init(void);
void ksmbd_workqueue_destroy(void);
bool ksmbd_queue_work(struct ksmbd_work *work);

int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
			   void *aux_buf, unsigned int aux_size);
int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
void ksmbd_iov_reset(struct ksmbd_work *work);
#endif /* __KSMBD_WORK_H__ */
Loading