Commit | Line | Data |
---|---|---|
0626e664 NJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (C) 2016 Namjae Jeon <namjae.jeon@protocolfreedom.org> | |
4 | * Copyright (C) 2018 Samsung Electronics Co., Ltd. | |
5 | */ | |
6 | ||
7 | #include <linux/mutex.h> | |
8 | #include <linux/freezer.h> | |
9 | #include <linux/module.h> | |
10 | ||
11 | #include "server.h" | |
0626e664 NJ |
12 | #include "smb_common.h" |
13 | #include "mgmt/ksmbd_ida.h" | |
14 | #include "connection.h" | |
15 | #include "transport_tcp.h" | |
16 | #include "transport_rdma.h" | |
17 | ||
18 | static DEFINE_MUTEX(init_lock); | |
19 | ||
20 | static struct ksmbd_conn_ops default_conn_ops; | |
21 | ||
d63528eb HL |
22 | LIST_HEAD(conn_list); |
23 | DEFINE_RWLOCK(conn_list_lock); | |
0626e664 NJ |
24 | |
25 | /** | |
26 | * ksmbd_conn_free() - free resources of the connection instance | |
27 | * | |
28 | * @conn: connection instance to be cleand up | |
29 | * | |
30 | * During the thread termination, the corresponding conn instance | |
31 | * resources(sock/memory) are released and finally the conn object is freed. | |
32 | */ | |
33 | void ksmbd_conn_free(struct ksmbd_conn *conn) | |
34 | { | |
35 | write_lock(&conn_list_lock); | |
36 | list_del(&conn->conns_list); | |
37 | write_unlock(&conn_list_lock); | |
38 | ||
e4d3e6b5 | 39 | xa_destroy(&conn->sessions); |
79f6b11a | 40 | kvfree(conn->request_buf); |
0626e664 NJ |
41 | kfree(conn->preauth_info); |
42 | kfree(conn); | |
43 | } | |
44 | ||
45 | /** | |
46 | * ksmbd_conn_alloc() - initialize a new connection instance | |
47 | * | |
48 | * Return: ksmbd_conn struct on success, otherwise NULL | |
49 | */ | |
50 | struct ksmbd_conn *ksmbd_conn_alloc(void) | |
51 | { | |
52 | struct ksmbd_conn *conn; | |
53 | ||
54 | conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); | |
55 | if (!conn) | |
56 | return NULL; | |
57 | ||
58 | conn->need_neg = true; | |
59 | conn->status = KSMBD_SESS_NEW; | |
60 | conn->local_nls = load_nls("utf8"); | |
61 | if (!conn->local_nls) | |
62 | conn->local_nls = load_nls_default(); | |
16b5f54e AH |
63 | if (IS_ENABLED(CONFIG_UNICODE)) |
64 | conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); | |
65 | else | |
66 | conn->um = ERR_PTR(-EOPNOTSUPP); | |
67 | if (IS_ERR(conn->um)) | |
68 | conn->um = NULL; | |
0626e664 NJ |
69 | atomic_set(&conn->req_running, 0); |
70 | atomic_set(&conn->r_count, 0); | |
bf8acc9e | 71 | conn->total_credits = 1; |
376b9133 | 72 | conn->outstanding_credits = 0; |
bf8acc9e | 73 | |
0626e664 | 74 | init_waitqueue_head(&conn->req_running_q); |
a14c5738 | 75 | init_waitqueue_head(&conn->r_count_q); |
0626e664 | 76 | INIT_LIST_HEAD(&conn->conns_list); |
0626e664 NJ |
77 | INIT_LIST_HEAD(&conn->requests); |
78 | INIT_LIST_HEAD(&conn->async_requests); | |
79 | spin_lock_init(&conn->request_lock); | |
80 | spin_lock_init(&conn->credits_lock); | |
d40012a8 | 81 | ida_init(&conn->async_ida); |
e4d3e6b5 | 82 | xa_init(&conn->sessions); |
0626e664 | 83 | |
d63528eb HL |
84 | spin_lock_init(&conn->llist_lock); |
85 | INIT_LIST_HEAD(&conn->lock_list); | |
86 | ||
0626e664 NJ |
87 | write_lock(&conn_list_lock); |
88 | list_add(&conn->conns_list, &conn_list); | |
89 | write_unlock(&conn_list_lock); | |
90 | return conn; | |
91 | } | |
92 | ||
93 | bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) | |
94 | { | |
95 | struct ksmbd_conn *t; | |
96 | bool ret = false; | |
97 | ||
98 | read_lock(&conn_list_lock); | |
99 | list_for_each_entry(t, &conn_list, conns_list) { | |
100 | if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) | |
101 | continue; | |
102 | ||
103 | ret = true; | |
104 | break; | |
105 | } | |
106 | read_unlock(&conn_list_lock); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | void ksmbd_conn_enqueue_request(struct ksmbd_work *work) | |
111 | { | |
112 | struct ksmbd_conn *conn = work->conn; | |
113 | struct list_head *requests_queue = NULL; | |
114 | ||
3a9b557f | 115 | if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) |
0626e664 | 116 | requests_queue = &conn->requests; |
0626e664 NJ |
117 | |
118 | if (requests_queue) { | |
119 | atomic_inc(&conn->req_running); | |
120 | spin_lock(&conn->request_lock); | |
121 | list_add_tail(&work->request_entry, requests_queue); | |
122 | spin_unlock(&conn->request_lock); | |
123 | } | |
124 | } | |
125 | ||
126 | int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) | |
127 | { | |
128 | struct ksmbd_conn *conn = work->conn; | |
129 | int ret = 1; | |
130 | ||
131 | if (list_empty(&work->request_entry) && | |
64b39f4a | 132 | list_empty(&work->async_request_entry)) |
0626e664 NJ |
133 | return 0; |
134 | ||
4b92841e HL |
135 | if (!work->multiRsp) |
136 | atomic_dec(&conn->req_running); | |
0626e664 | 137 | if (!work->multiRsp) { |
3a9b557f | 138 | spin_lock(&conn->request_lock); |
0626e664 | 139 | list_del_init(&work->request_entry); |
3a9b557f NJ |
140 | spin_unlock(&conn->request_lock); |
141 | if (work->asynchronous) | |
142 | release_async_work(work); | |
0626e664 NJ |
143 | ret = 0; |
144 | } | |
0626e664 NJ |
145 | |
146 | wake_up_all(&conn->req_running_q); | |
147 | return ret; | |
148 | } | |
149 | ||
150 | static void ksmbd_conn_lock(struct ksmbd_conn *conn) | |
151 | { | |
152 | mutex_lock(&conn->srv_mutex); | |
153 | } | |
154 | ||
155 | static void ksmbd_conn_unlock(struct ksmbd_conn *conn) | |
156 | { | |
157 | mutex_unlock(&conn->srv_mutex); | |
158 | } | |
159 | ||
160 | void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) | |
161 | { | |
162 | wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); | |
163 | } | |
164 | ||
165 | int ksmbd_conn_write(struct ksmbd_work *work) | |
166 | { | |
167 | struct ksmbd_conn *conn = work->conn; | |
0626e664 NJ |
168 | size_t len = 0; |
169 | int sent; | |
170 | struct kvec iov[3]; | |
171 | int iov_idx = 0; | |
172 | ||
cb451720 | 173 | if (!work->response_buf) { |
bde1694a | 174 | pr_err("NULL response header\n"); |
0626e664 NJ |
175 | return -EINVAL; |
176 | } | |
177 | ||
e5066499 | 178 | if (work->tr_buf) { |
0626e664 | 179 | iov[iov_idx] = (struct kvec) { work->tr_buf, |
2dd9129f | 180 | sizeof(struct smb2_transform_hdr) + 4 }; |
0626e664 NJ |
181 | len += iov[iov_idx++].iov_len; |
182 | } | |
183 | ||
e5066499 | 184 | if (work->aux_payload_sz) { |
cb451720 | 185 | iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; |
0626e664 | 186 | len += iov[iov_idx++].iov_len; |
e5066499 | 187 | iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; |
0626e664 NJ |
188 | len += iov[iov_idx++].iov_len; |
189 | } else { | |
e5066499 NJ |
190 | if (work->tr_buf) |
191 | iov[iov_idx].iov_len = work->resp_hdr_sz; | |
0626e664 | 192 | else |
cb451720 NJ |
193 | iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; |
194 | iov[iov_idx].iov_base = work->response_buf; | |
0626e664 NJ |
195 | len += iov[iov_idx++].iov_len; |
196 | } | |
197 | ||
198 | ksmbd_conn_lock(conn); | |
199 | sent = conn->transport->ops->writev(conn->transport, &iov[0], | |
200 | iov_idx, len, | |
201 | work->need_invalidate_rkey, | |
202 | work->remote_key); | |
203 | ksmbd_conn_unlock(conn); | |
204 | ||
205 | if (sent < 0) { | |
bde1694a | 206 | pr_err("Failed to send message: %d\n", sent); |
0626e664 NJ |
207 | return sent; |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
1807abcf HL |
213 | int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, |
214 | void *buf, unsigned int buflen, | |
215 | struct smb2_buffer_desc_v1 *desc, | |
216 | unsigned int desc_len) | |
0626e664 NJ |
217 | { |
218 | int ret = -EINVAL; | |
219 | ||
220 | if (conn->transport->ops->rdma_read) | |
221 | ret = conn->transport->ops->rdma_read(conn->transport, | |
070fb21e | 222 | buf, buflen, |
1807abcf | 223 | desc, desc_len); |
0626e664 NJ |
224 | return ret; |
225 | } | |
226 | ||
1807abcf HL |
227 | int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, |
228 | void *buf, unsigned int buflen, | |
229 | struct smb2_buffer_desc_v1 *desc, | |
230 | unsigned int desc_len) | |
0626e664 NJ |
231 | { |
232 | int ret = -EINVAL; | |
233 | ||
234 | if (conn->transport->ops->rdma_write) | |
235 | ret = conn->transport->ops->rdma_write(conn->transport, | |
070fb21e | 236 | buf, buflen, |
1807abcf | 237 | desc, desc_len); |
0626e664 NJ |
238 | return ret; |
239 | } | |
240 | ||
241 | bool ksmbd_conn_alive(struct ksmbd_conn *conn) | |
242 | { | |
243 | if (!ksmbd_server_running()) | |
244 | return false; | |
245 | ||
246 | if (conn->status == KSMBD_SESS_EXITING) | |
247 | return false; | |
248 | ||
249 | if (kthread_should_stop()) | |
250 | return false; | |
251 | ||
252 | if (atomic_read(&conn->stats.open_files_count) > 0) | |
253 | return true; | |
254 | ||
255 | /* | |
256 | * Stop current session if the time that get last request from client | |
a9c241d0 | 257 | * is bigger than deadtime user configured and opening file count is |
0626e664 NJ |
258 | * zero. |
259 | */ | |
260 | if (server_conf.deadtime > 0 && | |
64b39f4a | 261 | time_after(jiffies, conn->last_active + server_conf.deadtime)) { |
0626e664 | 262 | ksmbd_debug(CONN, "No response from client in %lu minutes\n", |
070fb21e | 263 | server_conf.deadtime / SMB_ECHO_INTERVAL); |
0626e664 NJ |
264 | return false; |
265 | } | |
266 | return true; | |
267 | } | |
268 | ||
269 | /** | |
270 | * ksmbd_conn_handler_loop() - session thread to listen on new smb requests | |
271 | * @p: connection instance | |
272 | * | |
273 | * One thread each per connection | |
274 | * | |
275 | * Return: 0 on success | |
276 | */ | |
277 | int ksmbd_conn_handler_loop(void *p) | |
278 | { | |
279 | struct ksmbd_conn *conn = (struct ksmbd_conn *)p; | |
280 | struct ksmbd_transport *t = conn->transport; | |
62c487b5 | 281 | unsigned int pdu_size, max_allowed_pdu_size; |
0626e664 NJ |
282 | char hdr_buf[4] = {0,}; |
283 | int size; | |
284 | ||
285 | mutex_init(&conn->srv_mutex); | |
286 | __module_get(THIS_MODULE); | |
287 | ||
288 | if (t->ops->prepare && t->ops->prepare(t)) | |
289 | goto out; | |
290 | ||
291 | conn->last_active = jiffies; | |
292 | while (ksmbd_conn_alive(conn)) { | |
293 | if (try_to_freeze()) | |
294 | continue; | |
295 | ||
79f6b11a | 296 | kvfree(conn->request_buf); |
0626e664 NJ |
297 | conn->request_buf = NULL; |
298 | ||
be6f42fa | 299 | size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); |
0626e664 NJ |
300 | if (size != sizeof(hdr_buf)) |
301 | break; | |
302 | ||
303 | pdu_size = get_rfc1002_len(hdr_buf); | |
304 | ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); | |
305 | ||
62c487b5 NJ |
306 | if (conn->status == KSMBD_SESS_GOOD) |
307 | max_allowed_pdu_size = | |
308 | SMB3_MAX_MSGSIZE + conn->vals->max_write_size; | |
309 | else | |
310 | max_allowed_pdu_size = SMB3_MAX_MSGSIZE; | |
311 | ||
312 | if (pdu_size > max_allowed_pdu_size) { | |
7a17c61e | 313 | pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", |
62c487b5 NJ |
314 | pdu_size, max_allowed_pdu_size, |
315 | conn->status); | |
316 | break; | |
317 | } | |
318 | ||
36399990 | 319 | /* |
39b291b8 | 320 | * Check maximum pdu size(0x00FFFFFF). |
36399990 | 321 | */ |
39b291b8 | 322 | if (pdu_size > MAX_STREAM_PROT_LEN) |
62c487b5 | 323 | break; |
0626e664 NJ |
324 | |
325 | /* 4 for rfc1002 length field */ | |
326 | size = pdu_size + 4; | |
e416ea62 | 327 | conn->request_buf = kvmalloc(size, GFP_KERNEL); |
0626e664 | 328 | if (!conn->request_buf) |
83dcedd5 | 329 | break; |
0626e664 NJ |
330 | |
331 | memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); | |
332 | if (!ksmbd_smb_request(conn)) | |
333 | break; | |
334 | ||
335 | /* | |
336 | * We already read 4 bytes to find out PDU size, now | |
337 | * read in PDU | |
338 | */ | |
be6f42fa | 339 | size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2); |
0626e664 | 340 | if (size < 0) { |
bde1694a | 341 | pr_err("sock_read failed: %d\n", size); |
0626e664 NJ |
342 | break; |
343 | } | |
344 | ||
345 | if (size != pdu_size) { | |
bde1694a NJ |
346 | pr_err("PDU error. Read: %d, Expected: %d\n", |
347 | size, pdu_size); | |
0626e664 NJ |
348 | continue; |
349 | } | |
350 | ||
351 | if (!default_conn_ops.process_fn) { | |
bde1694a | 352 | pr_err("No connection request callback\n"); |
0626e664 NJ |
353 | break; |
354 | } | |
355 | ||
356 | if (default_conn_ops.process_fn(conn)) { | |
bde1694a | 357 | pr_err("Cannot handle request\n"); |
0626e664 NJ |
358 | break; |
359 | } | |
360 | } | |
361 | ||
362 | out: | |
363 | /* Wait till all reference dropped to the Server object*/ | |
a14c5738 NJ |
364 | wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); |
365 | ||
0626e664 | 366 | |
16b5f54e AH |
367 | if (IS_ENABLED(CONFIG_UNICODE)) |
368 | utf8_unload(conn->um); | |
0626e664 NJ |
369 | unload_nls(conn->local_nls); |
370 | if (default_conn_ops.terminate_fn) | |
371 | default_conn_ops.terminate_fn(conn); | |
372 | t->ops->disconnect(t); | |
373 | module_put(THIS_MODULE); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) | |
378 | { | |
379 | default_conn_ops.process_fn = ops->process_fn; | |
380 | default_conn_ops.terminate_fn = ops->terminate_fn; | |
381 | } | |
382 | ||
383 | int ksmbd_conn_transport_init(void) | |
384 | { | |
385 | int ret; | |
386 | ||
387 | mutex_lock(&init_lock); | |
388 | ret = ksmbd_tcp_init(); | |
389 | if (ret) { | |
390 | pr_err("Failed to init TCP subsystem: %d\n", ret); | |
391 | goto out; | |
392 | } | |
393 | ||
394 | ret = ksmbd_rdma_init(); | |
395 | if (ret) { | |
0a427cc6 | 396 | pr_err("Failed to init RDMA subsystem: %d\n", ret); |
0626e664 NJ |
397 | goto out; |
398 | } | |
399 | out: | |
400 | mutex_unlock(&init_lock); | |
401 | return ret; | |
402 | } | |
403 | ||
404 | static void stop_sessions(void) | |
405 | { | |
406 | struct ksmbd_conn *conn; | |
136dff3a | 407 | struct ksmbd_transport *t; |
0626e664 NJ |
408 | |
409 | again: | |
410 | read_lock(&conn_list_lock); | |
411 | list_for_each_entry(conn, &conn_list, conns_list) { | |
412 | struct task_struct *task; | |
413 | ||
136dff3a YC |
414 | t = conn->transport; |
415 | task = t->handler; | |
0626e664 NJ |
416 | if (task) |
417 | ksmbd_debug(CONN, "Stop session handler %s/%d\n", | |
070fb21e | 418 | task->comm, task_pid_nr(task)); |
0626e664 | 419 | conn->status = KSMBD_SESS_EXITING; |
136dff3a YC |
420 | if (t->ops->shutdown) { |
421 | read_unlock(&conn_list_lock); | |
422 | t->ops->shutdown(t); | |
423 | read_lock(&conn_list_lock); | |
424 | } | |
0626e664 NJ |
425 | } |
426 | read_unlock(&conn_list_lock); | |
427 | ||
428 | if (!list_empty(&conn_list)) { | |
64b39f4a | 429 | schedule_timeout_interruptible(HZ / 10); /* 100ms */ |
0626e664 NJ |
430 | goto again; |
431 | } | |
432 | } | |
433 | ||
434 | void ksmbd_conn_transport_destroy(void) | |
435 | { | |
436 | mutex_lock(&init_lock); | |
437 | ksmbd_tcp_destroy(); | |
438 | ksmbd_rdma_destroy(); | |
439 | stop_sessions(); | |
440 | mutex_unlock(&init_lock); | |
441 | } |