Skip to content

Commit e2b76ab

Browse files
namjaejeonSteve French
authored and
Steve French
committed
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: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 084ba46 commit e2b76ab

12 files changed

+380
-370
lines changed

fs/smb/server/auth.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,11 +1029,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
10291029
{
10301030
struct scatterlist *sg;
10311031
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
1032-
int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0;
1032+
int i, *nr_entries, total_entries = 0, sg_idx = 0;
10331033

10341034
if (!nvec)
10351035
return NULL;
10361036

1037+
nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
1038+
if (!nr_entries)
1039+
return NULL;
1040+
10371041
for (i = 0; i < nvec - 1; i++) {
10381042
unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;
10391043

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

10531057
sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
1054-
if (!sg)
1058+
if (!sg) {
1059+
kfree(nr_entries);
10551060
return NULL;
1061+
}
10561062

10571063
sg_init_table(sg, total_entries);
10581064
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,
10861092
}
10871093
}
10881094
smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
1095+
kfree(nr_entries);
10891096
return sg;
10901097
}
10911098

fs/smb/server/connection.c

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -123,28 +123,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
123123
}
124124
}
125125

126-
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
126+
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
127127
{
128128
struct ksmbd_conn *conn = work->conn;
129-
int ret = 1;
130129

131130
if (list_empty(&work->request_entry) &&
132131
list_empty(&work->async_request_entry))
133-
return 0;
132+
return;
134133

135-
if (!work->multiRsp)
136-
atomic_dec(&conn->req_running);
137-
if (!work->multiRsp) {
138-
spin_lock(&conn->request_lock);
139-
list_del_init(&work->request_entry);
140-
spin_unlock(&conn->request_lock);
141-
if (work->asynchronous)
142-
release_async_work(work);
143-
ret = 0;
144-
}
134+
atomic_dec(&conn->req_running);
135+
spin_lock(&conn->request_lock);
136+
list_del_init(&work->request_entry);
137+
spin_unlock(&conn->request_lock);
138+
if (work->asynchronous)
139+
release_async_work(work);
145140

146141
wake_up_all(&conn->req_running_q);
147-
return ret;
148142
}
149143

150144
void ksmbd_conn_lock(struct ksmbd_conn *conn)
@@ -193,41 +187,22 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
193187
int ksmbd_conn_write(struct ksmbd_work *work)
194188
{
195189
struct ksmbd_conn *conn = work->conn;
196-
size_t len = 0;
197190
int sent;
198-
struct kvec iov[3];
199-
int iov_idx = 0;
200191

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

206-
if (work->tr_buf) {
207-
iov[iov_idx] = (struct kvec) { work->tr_buf,
208-
sizeof(struct smb2_transform_hdr) + 4 };
209-
len += iov[iov_idx++].iov_len;
210-
}
211-
212-
if (work->aux_payload_sz) {
213-
iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz };
214-
len += iov[iov_idx++].iov_len;
215-
iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz };
216-
len += iov[iov_idx++].iov_len;
217-
} else {
218-
if (work->tr_buf)
219-
iov[iov_idx].iov_len = work->resp_hdr_sz;
220-
else
221-
iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4;
222-
iov[iov_idx].iov_base = work->response_buf;
223-
len += iov[iov_idx++].iov_len;
224-
}
197+
if (work->send_no_response)
198+
return 0;
225199

226200
ksmbd_conn_lock(conn);
227-
sent = conn->transport->ops->writev(conn->transport, &iov[0],
228-
iov_idx, len,
229-
work->need_invalidate_rkey,
230-
work->remote_key);
201+
sent = conn->transport->ops->writev(conn->transport, work->iov,
202+
work->iov_cnt,
203+
get_rfc1002_len(work->iov[0].iov_base) + 4,
204+
work->need_invalidate_rkey,
205+
work->remote_key);
231206
ksmbd_conn_unlock(conn);
232207

233208
if (sent < 0) {

fs/smb/server/connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
158158
struct smb2_buffer_desc_v1 *desc,
159159
unsigned int desc_len);
160160
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
161-
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
161+
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
162162
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
163163
int ksmbd_conn_handler_loop(void *p);
164164
int ksmbd_conn_transport_init(void);

fs/smb/server/ksmbd_work.c

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,35 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
2727
INIT_LIST_HEAD(&work->async_request_entry);
2828
INIT_LIST_HEAD(&work->fp_entry);
2929
INIT_LIST_HEAD(&work->interim_entry);
30+
INIT_LIST_HEAD(&work->aux_read_list);
31+
work->iov_alloc_cnt = 4;
32+
work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
33+
GFP_KERNEL);
34+
if (!work->iov) {
35+
kmem_cache_free(work_cache, work);
36+
work = NULL;
37+
}
3038
}
3139
return work;
3240
}
3341

3442
void ksmbd_free_work_struct(struct ksmbd_work *work)
3543
{
44+
struct aux_read *ar, *tmp;
45+
3646
WARN_ON(work->saved_cred != NULL);
3747

3848
kvfree(work->response_buf);
39-
kvfree(work->aux_payload_buf);
49+
50+
list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {
51+
kvfree(ar->buf);
52+
list_del(&ar->entry);
53+
kfree(ar);
54+
}
55+
4056
kfree(work->tr_buf);
4157
kvfree(work->request_buf);
58+
kfree(work->iov);
4259
if (work->async_id)
4360
ksmbd_release_id(&work->conn->async_ida, work->async_id);
4461
kmem_cache_free(work_cache, work);
@@ -77,3 +94,75 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
7794
{
7895
return queue_work(ksmbd_wq, &work->work);
7996
}
97+
98+
static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
99+
unsigned int ib_len)
100+
{
101+
102+
if (work->iov_alloc_cnt <= work->iov_cnt) {
103+
struct kvec *new;
104+
105+
work->iov_alloc_cnt += 4;
106+
new = krealloc(work->iov,
107+
sizeof(struct kvec) * work->iov_alloc_cnt,
108+
GFP_KERNEL | __GFP_ZERO);
109+
if (!new)
110+
return -ENOMEM;
111+
work->iov = new;
112+
}
113+
114+
work->iov[++work->iov_idx].iov_base = ib;
115+
work->iov[work->iov_idx].iov_len = ib_len;
116+
work->iov_cnt++;
117+
118+
return 0;
119+
}
120+
121+
static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
122+
void *aux_buf, unsigned int aux_size)
123+
{
124+
/* Plus rfc_length size on first iov */
125+
if (!work->iov_idx) {
126+
work->iov[work->iov_idx].iov_base = work->response_buf;
127+
*(__be32 *)work->iov[0].iov_base = 0;
128+
work->iov[work->iov_idx].iov_len = 4;
129+
work->iov_cnt++;
130+
}
131+
132+
ksmbd_realloc_iov_pin(work, ib, len);
133+
inc_rfc1001_len(work->iov[0].iov_base, len);
134+
135+
if (aux_size) {
136+
struct aux_read *ar;
137+
138+
ksmbd_realloc_iov_pin(work, aux_buf, aux_size);
139+
inc_rfc1001_len(work->iov[0].iov_base, aux_size);
140+
141+
ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
142+
if (!ar)
143+
return -ENOMEM;
144+
145+
ar->buf = aux_buf;
146+
list_add(&ar->entry, &work->aux_read_list);
147+
}
148+
149+
return 0;
150+
}
151+
152+
int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)
153+
{
154+
return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);
155+
}
156+
157+
int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
158+
void *aux_buf, unsigned int aux_size)
159+
{
160+
return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
161+
}
162+
163+
void ksmbd_iov_reset(struct ksmbd_work *work)
164+
{
165+
work->iov_idx = 0;
166+
work->iov_cnt = 0;
167+
*(__be32 *)work->iov[0].iov_base = 0;
168+
}

fs/smb/server/ksmbd_work.h

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ enum {
1919
KSMBD_WORK_CLOSED,
2020
};
2121

22+
struct aux_read {
23+
void *buf;
24+
struct list_head entry;
25+
};
26+
2227
/* one of these for every pending CIFS request at the connection */
2328
struct ksmbd_work {
2429
/* Server corresponding to this mid */
@@ -31,13 +36,19 @@ struct ksmbd_work {
3136
/* Response buffer */
3237
void *response_buf;
3338

34-
/* Read data buffer */
35-
void *aux_payload_buf;
39+
struct list_head aux_read_list;
40+
41+
struct kvec *iov;
42+
int iov_alloc_cnt;
43+
int iov_cnt;
44+
int iov_idx;
3645

3746
/* Next cmd hdr in compound req buf*/
3847
int next_smb2_rcv_hdr_off;
3948
/* Next cmd hdr in compound rsp buf*/
4049
int next_smb2_rsp_hdr_off;
50+
/* Current cmd hdr in compound rsp buf*/
51+
int curr_smb2_rsp_hdr_off;
4152

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

5566
/* response smb header size */
56-
unsigned int resp_hdr_sz;
5767
unsigned int response_sz;
58-
/* Read data count */
59-
unsigned int aux_payload_sz;
6068

6169
void *tr_buf;
6270

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

104+
/**
105+
* ksmbd_resp_buf_curr - Get current buffer on compound response.
106+
* @work: smb work containing response buffer
107+
*/
108+
static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work)
109+
{
110+
return work->response_buf + work->curr_smb2_rsp_hdr_off + 4;
111+
}
112+
98113
/**
99114
* ksmbd_req_buf_next - Get next buffer on compound request.
100115
* @work: smb work containing response buffer
@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void);
113128
int ksmbd_workqueue_init(void);
114129
void ksmbd_workqueue_destroy(void);
115130
bool ksmbd_queue_work(struct ksmbd_work *work);
116-
131+
int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
132+
void *aux_buf, unsigned int aux_size);
133+
int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
134+
void ksmbd_iov_reset(struct ksmbd_work *work);
117135
#endif /* __KSMBD_WORK_H__ */

fs/smb/server/oplock.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
639639
{
640640
struct smb2_oplock_break *rsp = NULL;
641641
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
642-
struct ksmbd_conn *conn = work->conn;
643642
struct oplock_break_info *br_info = work->request_buf;
644643
struct smb2_hdr *rsp_hdr;
645644
struct ksmbd_file *fp;
@@ -656,8 +655,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
656655

657656
rsp_hdr = smb2_get_msg(work->response_buf);
658657
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
659-
*(__be32 *)work->response_buf =
660-
cpu_to_be32(conn->vals->header_size);
661658
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
662659
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
663660
rsp_hdr->CreditRequest = cpu_to_le16(0);
@@ -684,13 +681,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
684681
rsp->PersistentFid = fp->persistent_id;
685682
rsp->VolatileFid = fp->volatile_id;
686683

687-
inc_rfc1001_len(work->response_buf, 24);
684+
ksmbd_fd_put(work, fp);
685+
if (ksmbd_iov_pin_rsp(work, (void *)rsp,
686+
sizeof(struct smb2_oplock_break)))
687+
goto out;
688688

689689
ksmbd_debug(OPLOCK,
690690
"sending oplock break v_id %llu p_id = %llu lock level = %d\n",
691691
rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel);
692692

693-
ksmbd_fd_put(work, fp);
694693
ksmbd_conn_write(work);
695694

696695
out:
@@ -751,7 +750,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
751750
struct smb2_lease_break *rsp = NULL;
752751
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
753752
struct lease_break_info *br_info = work->request_buf;
754-
struct ksmbd_conn *conn = work->conn;
755753
struct smb2_hdr *rsp_hdr;
756754

757755
if (allocate_oplock_break_buf(work)) {
@@ -761,8 +759,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
761759

762760
rsp_hdr = smb2_get_msg(work->response_buf);
763761
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
764-
*(__be32 *)work->response_buf =
765-
cpu_to_be32(conn->vals->header_size);
766762
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
767763
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
768764
rsp_hdr->CreditRequest = cpu_to_le16(0);
@@ -791,7 +787,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
791787
rsp->AccessMaskHint = 0;
792788
rsp->ShareMaskHint = 0;
793789

794-
inc_rfc1001_len(work->response_buf, 44);
790+
if (ksmbd_iov_pin_rsp(work, (void *)rsp,
791+
sizeof(struct smb2_lease_break)))
792+
goto out;
795793

796794
ksmbd_conn_write(work);
797795

@@ -845,6 +843,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
845843
setup_async_work(in_work, NULL, NULL);
846844
smb2_send_interim_resp(in_work, STATUS_PENDING);
847845
list_del(&in_work->interim_entry);
846+
ksmbd_iov_reset(in_work);
848847
}
849848
INIT_WORK(&work->work, __smb2_lease_break_noti);
850849
ksmbd_queue_work(work);

0 commit comments

Comments
 (0)