]>
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(); | |
63 | atomic_set(&conn->req_running, 0); | |
64 | atomic_set(&conn->r_count, 0); | |
bf8acc9e | 65 | conn->total_credits = 1; |
376b9133 | 66 | conn->outstanding_credits = 0; |
bf8acc9e | 67 | |
0626e664 | 68 | init_waitqueue_head(&conn->req_running_q); |
a14c5738 | 69 | init_waitqueue_head(&conn->r_count_q); |
0626e664 | 70 | INIT_LIST_HEAD(&conn->conns_list); |
0626e664 NJ |
71 | INIT_LIST_HEAD(&conn->requests); |
72 | INIT_LIST_HEAD(&conn->async_requests); | |
73 | spin_lock_init(&conn->request_lock); | |
74 | spin_lock_init(&conn->credits_lock); | |
d40012a8 | 75 | ida_init(&conn->async_ida); |
e4d3e6b5 | 76 | xa_init(&conn->sessions); |
0626e664 | 77 | |
d63528eb HL |
78 | spin_lock_init(&conn->llist_lock); |
79 | INIT_LIST_HEAD(&conn->lock_list); | |
80 | ||
0626e664 NJ |
81 | write_lock(&conn_list_lock); |
82 | list_add(&conn->conns_list, &conn_list); | |
83 | write_unlock(&conn_list_lock); | |
84 | return conn; | |
85 | } | |
86 | ||
87 | bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) | |
88 | { | |
89 | struct ksmbd_conn *t; | |
90 | bool ret = false; | |
91 | ||
92 | read_lock(&conn_list_lock); | |
93 | list_for_each_entry(t, &conn_list, conns_list) { | |
94 | if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) | |
95 | continue; | |
96 | ||
97 | ret = true; | |
98 | break; | |
99 | } | |
100 | read_unlock(&conn_list_lock); | |
101 | return ret; | |
102 | } | |
103 | ||
104 | void ksmbd_conn_enqueue_request(struct ksmbd_work *work) | |
105 | { | |
106 | struct ksmbd_conn *conn = work->conn; | |
107 | struct list_head *requests_queue = NULL; | |
108 | ||
109 | if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { | |
110 | requests_queue = &conn->requests; | |
111 | work->syncronous = true; | |
112 | } | |
113 | ||
114 | if (requests_queue) { | |
115 | atomic_inc(&conn->req_running); | |
116 | spin_lock(&conn->request_lock); | |
117 | list_add_tail(&work->request_entry, requests_queue); | |
118 | spin_unlock(&conn->request_lock); | |
119 | } | |
120 | } | |
121 | ||
122 | int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) | |
123 | { | |
124 | struct ksmbd_conn *conn = work->conn; | |
125 | int ret = 1; | |
126 | ||
127 | if (list_empty(&work->request_entry) && | |
64b39f4a | 128 | list_empty(&work->async_request_entry)) |
0626e664 NJ |
129 | return 0; |
130 | ||
4b92841e HL |
131 | if (!work->multiRsp) |
132 | atomic_dec(&conn->req_running); | |
0626e664 NJ |
133 | spin_lock(&conn->request_lock); |
134 | if (!work->multiRsp) { | |
135 | list_del_init(&work->request_entry); | |
136 | if (work->syncronous == false) | |
137 | list_del_init(&work->async_request_entry); | |
138 | ret = 0; | |
139 | } | |
140 | spin_unlock(&conn->request_lock); | |
141 | ||
142 | wake_up_all(&conn->req_running_q); | |
143 | return ret; | |
144 | } | |
145 | ||
146 | static void ksmbd_conn_lock(struct ksmbd_conn *conn) | |
147 | { | |
148 | mutex_lock(&conn->srv_mutex); | |
149 | } | |
150 | ||
151 | static void ksmbd_conn_unlock(struct ksmbd_conn *conn) | |
152 | { | |
153 | mutex_unlock(&conn->srv_mutex); | |
154 | } | |
155 | ||
156 | void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) | |
157 | { | |
158 | wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); | |
159 | } | |
160 | ||
161 | int ksmbd_conn_write(struct ksmbd_work *work) | |
162 | { | |
163 | struct ksmbd_conn *conn = work->conn; | |
0626e664 NJ |
164 | size_t len = 0; |
165 | int sent; | |
166 | struct kvec iov[3]; | |
167 | int iov_idx = 0; | |
168 | ||
cb451720 | 169 | if (!work->response_buf) { |
bde1694a | 170 | pr_err("NULL response header\n"); |
0626e664 NJ |
171 | return -EINVAL; |
172 | } | |
173 | ||
e5066499 | 174 | if (work->tr_buf) { |
0626e664 | 175 | iov[iov_idx] = (struct kvec) { work->tr_buf, |
2dd9129f | 176 | sizeof(struct smb2_transform_hdr) + 4 }; |
0626e664 NJ |
177 | len += iov[iov_idx++].iov_len; |
178 | } | |
179 | ||
e5066499 | 180 | if (work->aux_payload_sz) { |
cb451720 | 181 | iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; |
0626e664 | 182 | len += iov[iov_idx++].iov_len; |
e5066499 | 183 | iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; |
0626e664 NJ |
184 | len += iov[iov_idx++].iov_len; |
185 | } else { | |
e5066499 NJ |
186 | if (work->tr_buf) |
187 | iov[iov_idx].iov_len = work->resp_hdr_sz; | |
0626e664 | 188 | else |
cb451720 NJ |
189 | iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; |
190 | iov[iov_idx].iov_base = work->response_buf; | |
0626e664 NJ |
191 | len += iov[iov_idx++].iov_len; |
192 | } | |
193 | ||
194 | ksmbd_conn_lock(conn); | |
195 | sent = conn->transport->ops->writev(conn->transport, &iov[0], | |
196 | iov_idx, len, | |
197 | work->need_invalidate_rkey, | |
198 | work->remote_key); | |
199 | ksmbd_conn_unlock(conn); | |
200 | ||
201 | if (sent < 0) { | |
bde1694a | 202 | pr_err("Failed to send message: %d\n", sent); |
0626e664 NJ |
203 | return sent; |
204 | } | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
1807abcf HL |
209 | int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, |
210 | void *buf, unsigned int buflen, | |
211 | struct smb2_buffer_desc_v1 *desc, | |
212 | unsigned int desc_len) | |
0626e664 NJ |
213 | { |
214 | int ret = -EINVAL; | |
215 | ||
216 | if (conn->transport->ops->rdma_read) | |
217 | ret = conn->transport->ops->rdma_read(conn->transport, | |
070fb21e | 218 | buf, buflen, |
1807abcf | 219 | desc, desc_len); |
0626e664 NJ |
220 | return ret; |
221 | } | |
222 | ||
1807abcf HL |
223 | int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, |
224 | void *buf, unsigned int buflen, | |
225 | struct smb2_buffer_desc_v1 *desc, | |
226 | unsigned int desc_len) | |
0626e664 NJ |
227 | { |
228 | int ret = -EINVAL; | |
229 | ||
230 | if (conn->transport->ops->rdma_write) | |
231 | ret = conn->transport->ops->rdma_write(conn->transport, | |
070fb21e | 232 | buf, buflen, |
1807abcf | 233 | desc, desc_len); |
0626e664 NJ |
234 | return ret; |
235 | } | |
236 | ||
237 | bool ksmbd_conn_alive(struct ksmbd_conn *conn) | |
238 | { | |
239 | if (!ksmbd_server_running()) | |
240 | return false; | |
241 | ||
242 | if (conn->status == KSMBD_SESS_EXITING) | |
243 | return false; | |
244 | ||
245 | if (kthread_should_stop()) | |
246 | return false; | |
247 | ||
248 | if (atomic_read(&conn->stats.open_files_count) > 0) | |
249 | return true; | |
250 | ||
251 | /* | |
252 | * Stop current session if the time that get last request from client | |
a9c241d0 | 253 | * is bigger than deadtime user configured and opening file count is |
0626e664 NJ |
254 | * zero. |
255 | */ | |
256 | if (server_conf.deadtime > 0 && | |
64b39f4a | 257 | time_after(jiffies, conn->last_active + server_conf.deadtime)) { |
0626e664 | 258 | ksmbd_debug(CONN, "No response from client in %lu minutes\n", |
070fb21e | 259 | server_conf.deadtime / SMB_ECHO_INTERVAL); |
0626e664 NJ |
260 | return false; |
261 | } | |
262 | return true; | |
263 | } | |
264 | ||
265 | /** | |
266 | * ksmbd_conn_handler_loop() - session thread to listen on new smb requests | |
267 | * @p: connection instance | |
268 | * | |
269 | * One thread each per connection | |
270 | * | |
271 | * Return: 0 on success | |
272 | */ | |
273 | int ksmbd_conn_handler_loop(void *p) | |
274 | { | |
275 | struct ksmbd_conn *conn = (struct ksmbd_conn *)p; | |
276 | struct ksmbd_transport *t = conn->transport; | |
277 | unsigned int pdu_size; | |
278 | char hdr_buf[4] = {0,}; | |
279 | int size; | |
280 | ||
281 | mutex_init(&conn->srv_mutex); | |
282 | __module_get(THIS_MODULE); | |
283 | ||
284 | if (t->ops->prepare && t->ops->prepare(t)) | |
285 | goto out; | |
286 | ||
287 | conn->last_active = jiffies; | |
288 | while (ksmbd_conn_alive(conn)) { | |
289 | if (try_to_freeze()) | |
290 | continue; | |
291 | ||
79f6b11a | 292 | kvfree(conn->request_buf); |
0626e664 NJ |
293 | conn->request_buf = NULL; |
294 | ||
295 | size = t->ops->read(t, hdr_buf, sizeof(hdr_buf)); | |
296 | if (size != sizeof(hdr_buf)) | |
297 | break; | |
298 | ||
299 | pdu_size = get_rfc1002_len(hdr_buf); | |
300 | ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); | |
301 | ||
36399990 NJ |
302 | /* |
303 | * Check if pdu size is valid (min : smb header size, | |
304 | * max : 0x00FFFFFF). | |
305 | */ | |
306 | if (pdu_size < __SMB2_HEADER_STRUCTURE_SIZE || | |
307 | pdu_size > MAX_STREAM_PROT_LEN) { | |
0626e664 NJ |
308 | continue; |
309 | } | |
310 | ||
311 | /* 4 for rfc1002 length field */ | |
312 | size = pdu_size + 4; | |
79f6b11a | 313 | conn->request_buf = kvmalloc(size, GFP_KERNEL); |
0626e664 NJ |
314 | if (!conn->request_buf) |
315 | continue; | |
316 | ||
317 | memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); | |
318 | if (!ksmbd_smb_request(conn)) | |
319 | break; | |
320 | ||
321 | /* | |
322 | * We already read 4 bytes to find out PDU size, now | |
323 | * read in PDU | |
324 | */ | |
325 | size = t->ops->read(t, conn->request_buf + 4, pdu_size); | |
326 | if (size < 0) { | |
bde1694a | 327 | pr_err("sock_read failed: %d\n", size); |
0626e664 NJ |
328 | break; |
329 | } | |
330 | ||
331 | if (size != pdu_size) { | |
bde1694a NJ |
332 | pr_err("PDU error. Read: %d, Expected: %d\n", |
333 | size, pdu_size); | |
0626e664 NJ |
334 | continue; |
335 | } | |
336 | ||
337 | if (!default_conn_ops.process_fn) { | |
bde1694a | 338 | pr_err("No connection request callback\n"); |
0626e664 NJ |
339 | break; |
340 | } | |
341 | ||
342 | if (default_conn_ops.process_fn(conn)) { | |
bde1694a | 343 | pr_err("Cannot handle request\n"); |
0626e664 NJ |
344 | break; |
345 | } | |
346 | } | |
347 | ||
348 | out: | |
349 | /* Wait till all reference dropped to the Server object*/ | |
a14c5738 NJ |
350 | wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); |
351 | ||
0626e664 NJ |
352 | |
353 | unload_nls(conn->local_nls); | |
354 | if (default_conn_ops.terminate_fn) | |
355 | default_conn_ops.terminate_fn(conn); | |
356 | t->ops->disconnect(t); | |
357 | module_put(THIS_MODULE); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) | |
362 | { | |
363 | default_conn_ops.process_fn = ops->process_fn; | |
364 | default_conn_ops.terminate_fn = ops->terminate_fn; | |
365 | } | |
366 | ||
367 | int ksmbd_conn_transport_init(void) | |
368 | { | |
369 | int ret; | |
370 | ||
371 | mutex_lock(&init_lock); | |
372 | ret = ksmbd_tcp_init(); | |
373 | if (ret) { | |
374 | pr_err("Failed to init TCP subsystem: %d\n", ret); | |
375 | goto out; | |
376 | } | |
377 | ||
378 | ret = ksmbd_rdma_init(); | |
379 | if (ret) { | |
0a427cc6 | 380 | pr_err("Failed to init RDMA subsystem: %d\n", ret); |
0626e664 NJ |
381 | goto out; |
382 | } | |
383 | out: | |
384 | mutex_unlock(&init_lock); | |
385 | return ret; | |
386 | } | |
387 | ||
388 | static void stop_sessions(void) | |
389 | { | |
390 | struct ksmbd_conn *conn; | |
136dff3a | 391 | struct ksmbd_transport *t; |
0626e664 NJ |
392 | |
393 | again: | |
394 | read_lock(&conn_list_lock); | |
395 | list_for_each_entry(conn, &conn_list, conns_list) { | |
396 | struct task_struct *task; | |
397 | ||
136dff3a YC |
398 | t = conn->transport; |
399 | task = t->handler; | |
0626e664 NJ |
400 | if (task) |
401 | ksmbd_debug(CONN, "Stop session handler %s/%d\n", | |
070fb21e | 402 | task->comm, task_pid_nr(task)); |
0626e664 | 403 | conn->status = KSMBD_SESS_EXITING; |
136dff3a YC |
404 | if (t->ops->shutdown) { |
405 | read_unlock(&conn_list_lock); | |
406 | t->ops->shutdown(t); | |
407 | read_lock(&conn_list_lock); | |
408 | } | |
0626e664 NJ |
409 | } |
410 | read_unlock(&conn_list_lock); | |
411 | ||
412 | if (!list_empty(&conn_list)) { | |
64b39f4a | 413 | schedule_timeout_interruptible(HZ / 10); /* 100ms */ |
0626e664 NJ |
414 | goto again; |
415 | } | |
416 | } | |
417 | ||
418 | void ksmbd_conn_transport_destroy(void) | |
419 | { | |
420 | mutex_lock(&init_lock); | |
421 | ksmbd_tcp_destroy(); | |
422 | ksmbd_rdma_destroy(); | |
423 | stop_sessions(); | |
424 | mutex_unlock(&init_lock); | |
425 | } |