]>
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 | ||
79f6b11a | 39 | kvfree(conn->request_buf); |
0626e664 NJ |
40 | kfree(conn->preauth_info); |
41 | kfree(conn); | |
42 | } | |
43 | ||
44 | /** | |
45 | * ksmbd_conn_alloc() - initialize a new connection instance | |
46 | * | |
47 | * Return: ksmbd_conn struct on success, otherwise NULL | |
48 | */ | |
49 | struct ksmbd_conn *ksmbd_conn_alloc(void) | |
50 | { | |
51 | struct ksmbd_conn *conn; | |
52 | ||
53 | conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); | |
54 | if (!conn) | |
55 | return NULL; | |
56 | ||
57 | conn->need_neg = true; | |
58 | conn->status = KSMBD_SESS_NEW; | |
59 | conn->local_nls = load_nls("utf8"); | |
60 | if (!conn->local_nls) | |
61 | conn->local_nls = load_nls_default(); | |
62 | atomic_set(&conn->req_running, 0); | |
63 | atomic_set(&conn->r_count, 0); | |
bf8acc9e | 64 | conn->total_credits = 1; |
c5c27f50 | 65 | conn->outstanding_credits = 0; |
bf8acc9e | 66 | |
0626e664 NJ |
67 | init_waitqueue_head(&conn->req_running_q); |
68 | INIT_LIST_HEAD(&conn->conns_list); | |
69 | INIT_LIST_HEAD(&conn->sessions); | |
70 | INIT_LIST_HEAD(&conn->requests); | |
71 | INIT_LIST_HEAD(&conn->async_requests); | |
72 | spin_lock_init(&conn->request_lock); | |
73 | spin_lock_init(&conn->credits_lock); | |
d40012a8 | 74 | ida_init(&conn->async_ida); |
0626e664 | 75 | |
d63528eb HL |
76 | spin_lock_init(&conn->llist_lock); |
77 | INIT_LIST_HEAD(&conn->lock_list); | |
78 | ||
0626e664 NJ |
79 | write_lock(&conn_list_lock); |
80 | list_add(&conn->conns_list, &conn_list); | |
81 | write_unlock(&conn_list_lock); | |
82 | return conn; | |
83 | } | |
84 | ||
85 | bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) | |
86 | { | |
87 | struct ksmbd_conn *t; | |
88 | bool ret = false; | |
89 | ||
90 | read_lock(&conn_list_lock); | |
91 | list_for_each_entry(t, &conn_list, conns_list) { | |
92 | if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) | |
93 | continue; | |
94 | ||
95 | ret = true; | |
96 | break; | |
97 | } | |
98 | read_unlock(&conn_list_lock); | |
99 | return ret; | |
100 | } | |
101 | ||
102 | void ksmbd_conn_enqueue_request(struct ksmbd_work *work) | |
103 | { | |
104 | struct ksmbd_conn *conn = work->conn; | |
105 | struct list_head *requests_queue = NULL; | |
106 | ||
107 | if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { | |
108 | requests_queue = &conn->requests; | |
109 | work->syncronous = true; | |
110 | } | |
111 | ||
112 | if (requests_queue) { | |
113 | atomic_inc(&conn->req_running); | |
114 | spin_lock(&conn->request_lock); | |
115 | list_add_tail(&work->request_entry, requests_queue); | |
116 | spin_unlock(&conn->request_lock); | |
117 | } | |
118 | } | |
119 | ||
120 | int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) | |
121 | { | |
122 | struct ksmbd_conn *conn = work->conn; | |
123 | int ret = 1; | |
124 | ||
125 | if (list_empty(&work->request_entry) && | |
64b39f4a | 126 | list_empty(&work->async_request_entry)) |
0626e664 NJ |
127 | return 0; |
128 | ||
4b92841e HL |
129 | if (!work->multiRsp) |
130 | atomic_dec(&conn->req_running); | |
0626e664 NJ |
131 | spin_lock(&conn->request_lock); |
132 | if (!work->multiRsp) { | |
133 | list_del_init(&work->request_entry); | |
134 | if (work->syncronous == false) | |
135 | list_del_init(&work->async_request_entry); | |
136 | ret = 0; | |
137 | } | |
138 | spin_unlock(&conn->request_lock); | |
139 | ||
140 | wake_up_all(&conn->req_running_q); | |
141 | return ret; | |
142 | } | |
143 | ||
144 | static void ksmbd_conn_lock(struct ksmbd_conn *conn) | |
145 | { | |
146 | mutex_lock(&conn->srv_mutex); | |
147 | } | |
148 | ||
149 | static void ksmbd_conn_unlock(struct ksmbd_conn *conn) | |
150 | { | |
151 | mutex_unlock(&conn->srv_mutex); | |
152 | } | |
153 | ||
154 | void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) | |
155 | { | |
156 | wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); | |
157 | } | |
158 | ||
159 | int ksmbd_conn_write(struct ksmbd_work *work) | |
160 | { | |
161 | struct ksmbd_conn *conn = work->conn; | |
e5066499 | 162 | struct smb_hdr *rsp_hdr = work->response_buf; |
0626e664 NJ |
163 | size_t len = 0; |
164 | int sent; | |
165 | struct kvec iov[3]; | |
166 | int iov_idx = 0; | |
167 | ||
168 | ksmbd_conn_try_dequeue_request(work); | |
169 | if (!rsp_hdr) { | |
bde1694a | 170 | pr_err("NULL response header\n"); |
0626e664 NJ |
171 | return -EINVAL; |
172 | } | |
173 | ||
e5066499 | 174 | if (work->tr_buf) { |
0626e664 NJ |
175 | iov[iov_idx] = (struct kvec) { work->tr_buf, |
176 | sizeof(struct smb2_transform_hdr) }; | |
177 | len += iov[iov_idx++].iov_len; | |
178 | } | |
179 | ||
e5066499 NJ |
180 | if (work->aux_payload_sz) { |
181 | iov[iov_idx] = (struct kvec) { rsp_hdr, 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 NJ |
188 | else |
189 | iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; | |
190 | iov[iov_idx].iov_base = rsp_hdr; | |
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 | ||
64b39f4a | 209 | int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, |
070fb21e NJ |
210 | unsigned int buflen, u32 remote_key, u64 remote_offset, |
211 | u32 remote_len) | |
0626e664 NJ |
212 | { |
213 | int ret = -EINVAL; | |
214 | ||
215 | if (conn->transport->ops->rdma_read) | |
216 | ret = conn->transport->ops->rdma_read(conn->transport, | |
070fb21e NJ |
217 | buf, buflen, |
218 | remote_key, remote_offset, | |
219 | remote_len); | |
0626e664 NJ |
220 | return ret; |
221 | } | |
222 | ||
64b39f4a | 223 | int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, |
070fb21e NJ |
224 | unsigned int buflen, u32 remote_key, |
225 | u64 remote_offset, u32 remote_len) | |
0626e664 NJ |
226 | { |
227 | int ret = -EINVAL; | |
228 | ||
229 | if (conn->transport->ops->rdma_write) | |
230 | ret = conn->transport->ops->rdma_write(conn->transport, | |
070fb21e NJ |
231 | buf, buflen, |
232 | remote_key, remote_offset, | |
233 | remote_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*/ | |
350 | while (atomic_read(&conn->r_count) > 0) | |
351 | schedule_timeout(HZ); | |
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; | |
391 | ||
392 | again: | |
393 | read_lock(&conn_list_lock); | |
394 | list_for_each_entry(conn, &conn_list, conns_list) { | |
395 | struct task_struct *task; | |
396 | ||
397 | task = conn->transport->handler; | |
398 | if (task) | |
399 | ksmbd_debug(CONN, "Stop session handler %s/%d\n", | |
070fb21e | 400 | task->comm, task_pid_nr(task)); |
0626e664 NJ |
401 | conn->status = KSMBD_SESS_EXITING; |
402 | } | |
403 | read_unlock(&conn_list_lock); | |
404 | ||
405 | if (!list_empty(&conn_list)) { | |
64b39f4a | 406 | schedule_timeout_interruptible(HZ / 10); /* 100ms */ |
0626e664 NJ |
407 | goto again; |
408 | } | |
409 | } | |
410 | ||
411 | void ksmbd_conn_transport_destroy(void) | |
412 | { | |
413 | mutex_lock(&init_lock); | |
414 | ksmbd_tcp_destroy(); | |
415 | ksmbd_rdma_destroy(); | |
416 | stop_sessions(); | |
417 | mutex_unlock(&init_lock); | |
418 | } |