| 1 | |
| 2 | |
| 3 | * Copyright (C) 2016 Namjae Jeon <[email protected]>
|
| 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"
|
| 12 | #include "smb_common.h"
|
| 13 | #include "mgmt/ksmbd_ida.h"
|
| 14 | #include "mgmt/user_session.h"
|
| 15 | #include "connection.h"
|
| 16 | #include "vfs_cache.h"
|
| 17 | #include "compress.h"
|
| 18 | #include "transport_tcp.h"
|
| 19 | #include "transport_rdma.h"
|
| 20 | #include "misc.h"
|
| 21 |
|
| 22 | static DEFINE_MUTEX(init_lock);
|
| 23 |
|
| 24 | static struct ksmbd_conn_ops default_conn_ops;
|
| 25 | static struct delayed_work session_expiration_work;
|
| 26 | static bool stopping_session_expiration_work;
|
| 27 |
|
| 28 | DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
|
| 29 | DECLARE_RWSEM(conn_list_lock);
|
| 30 |
|
| 31 | #ifdef CONFIG_PROC_FS
|
| 32 | static struct proc_dir_entry *proc_clients;
|
| 33 |
|
| 34 | static const char *ksmbd_conn_state_string(struct ksmbd_conn *conn)
|
| 35 | {
|
| 36 | switch (READ_ONCE(conn->status)) {
|
| 37 | case KSMBD_SESS_NEW:
|
| 38 | return "new";
|
| 39 | case KSMBD_SESS_GOOD:
|
| 40 | return "good";
|
| 41 | case KSMBD_SESS_EXITING:
|
| 42 | return "exiting";
|
| 43 | case KSMBD_SESS_NEED_RECONNECT:
|
| 44 | return "reconnect";
|
| 45 | case KSMBD_SESS_NEED_NEGOTIATE:
|
| 46 | return "negotiate";
|
| 47 | case KSMBD_SESS_NEED_SETUP:
|
| 48 | return "setup";
|
| 49 | case KSMBD_SESS_RELEASING:
|
| 50 | return "releasing";
|
| 51 | default:
|
| 52 | return "unknown";
|
| 53 | }
|
| 54 | }
|
| 55 |
|
| 56 | static const char *ksmbd_conn_transport_string(struct ksmbd_conn *conn)
|
| 57 | {
|
| 58 | if (conn->transport->ops->rdma_read || conn->transport->ops->rdma_write)
|
| 59 | return "smbdirect";
|
| 60 | return "tcp";
|
| 61 | }
|
| 62 |
|
| 63 | static void proc_show_conn_feature(struct seq_file *m, bool *separator,
|
| 64 | bool enabled, const char *name)
|
| 65 | {
|
| 66 | if (!enabled)
|
| 67 | return;
|
| 68 | seq_printf(m, "%s%s", *separator ? "," : "", name);
|
| 69 | *separator = true;
|
| 70 | }
|
| 71 |
|
| 72 | static void proc_show_conn_features(struct seq_file *m,
|
| 73 | struct ksmbd_conn *conn)
|
| 74 | {
|
| 75 | bool separator = false;
|
| 76 |
|
| 77 | proc_show_conn_feature(m, &separator,
|
| 78 | conn->sign || conn->signing_negotiated, "sign");
|
| 79 | proc_show_conn_feature(m, &separator, conn->cipher_type, "encrypt");
|
| 80 | proc_show_conn_feature(m, &separator,
|
| 81 | conn->compress_algorithm != SMB3_COMPRESS_NONE,
|
| 82 | "compress");
|
| 83 | proc_show_conn_feature(m, &separator, conn->rdma_transform_ids,
|
| 84 | "rdma-transform");
|
| 85 | proc_show_conn_feature(m, &separator, conn->posix_ext_supported, "posix");
|
| 86 | if (!separator)
|
| 87 | seq_puts(m, "none");
|
| 88 | }
|
| 89 |
|
| 90 | static int proc_show_clients(struct seq_file *m, void *v)
|
| 91 | {
|
| 92 | struct ksmbd_conn *conn;
|
| 93 | struct timespec64 now, t;
|
| 94 | int i;
|
| 95 |
|
| 96 | down_read(&conn_list_lock);
|
| 97 | hash_for_each(conn_list, i, conn, hlist) {
|
| 98 | unsigned int outstanding_credits, total_credits;
|
| 99 | unsigned long id;
|
| 100 | void *entry;
|
| 101 | unsigned int sessions = 0;
|
| 102 |
|
| 103 | jiffies_to_timespec64(jiffies - conn->last_active, &t);
|
| 104 | ktime_get_real_ts64(&now);
|
| 105 | t = timespec64_sub(now, t);
|
| 106 |
|
| 107 | spin_lock(&conn->credits_lock);
|
| 108 | outstanding_credits = conn->outstanding_credits;
|
| 109 | total_credits = conn->total_credits;
|
| 110 | spin_unlock(&conn->credits_lock);
|
| 111 |
|
| 112 | rcu_read_lock();
|
| 113 | xa_for_each(&conn->sessions, id, entry)
|
| 114 | sessions++;
|
| 115 | rcu_read_unlock();
|
| 116 | #if IS_ENABLED(CONFIG_IPV6)
|
| 117 | if (!conn->inet_addr)
|
| 118 | seq_printf(m, "client:\t%pI6c\n", &conn->inet6_addr);
|
| 119 | else
|
| 120 | #endif
|
| 121 | seq_printf(m, "client:\t%pI4\n", &conn->inet_addr);
|
| 122 | seq_printf(m, "transport:\t%s\n", ksmbd_conn_transport_string(conn));
|
| 123 | seq_printf(m, "state:\t%s\n", ksmbd_conn_state_string(conn));
|
| 124 | seq_printf(m, "dialect:\t0x%04x\n", conn->dialect);
|
| 125 | seq_printf(m, "credits:\t%u/%u\n", outstanding_credits,
|
| 126 | total_credits);
|
| 127 | seq_printf(m, "sessions:\t%u\n", sessions);
|
| 128 | seq_printf(m, "open_files:\t%d\n",
|
| 129 | atomic_read(&conn->stats.open_files_count));
|
| 130 | seq_printf(m, "requests:\t%lld\n",
|
| 131 | atomic64_read(&conn->stats.request_served));
|
| 132 | seq_puts(m, "features:\t");
|
| 133 | proc_show_conn_features(m, conn);
|
| 134 | seq_printf(m, "\nlast_active:\t%ptT\n\n", &t);
|
| 135 | }
|
| 136 | up_read(&conn_list_lock);
|
| 137 | return 0;
|
| 138 | }
|
| 139 |
|
| 140 | static int create_proc_clients(void)
|
| 141 | {
|
| 142 | proc_clients = ksmbd_proc_create("clients",
|
| 143 | proc_show_clients, NULL);
|
| 144 | if (!proc_clients)
|
| 145 | return -ENOMEM;
|
| 146 | return 0;
|
| 147 | }
|
| 148 |
|
| 149 | static void delete_proc_clients(void)
|
| 150 | {
|
| 151 | if (proc_clients) {
|
| 152 | proc_remove(proc_clients);
|
| 153 | proc_clients = NULL;
|
| 154 | }
|
| 155 | }
|
| 156 | #else
|
| 157 | static int create_proc_clients(void) { return 0; }
|
| 158 | static void delete_proc_clients(void) {}
|
| 159 | #endif
|
| 160 |
|
| 161 | static struct workqueue_struct *ksmbd_conn_wq;
|
| 162 |
|
| 163 | static void ksmbd_session_expiration_worker(struct work_struct *work);
|
| 164 |
|
| 165 | int ksmbd_conn_wq_init(void)
|
| 166 | {
|
| 167 | ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release",
|
| 168 | WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
|
| 169 | if (!ksmbd_conn_wq)
|
| 170 | return -ENOMEM;
|
| 171 |
|
| 172 | WRITE_ONCE(stopping_session_expiration_work, false);
|
| 173 | INIT_DELAYED_WORK(&session_expiration_work,
|
| 174 | ksmbd_session_expiration_worker);
|
| 175 | queue_delayed_work(ksmbd_conn_wq, &session_expiration_work,
|
| 176 | KSMBD_SESSION_EXPIRATION_INTERVAL);
|
| 177 | return 0;
|
| 178 | }
|
| 179 |
|
| 180 | void ksmbd_conn_wq_destroy(void)
|
| 181 | {
|
| 182 | if (ksmbd_conn_wq) {
|
| 183 | WRITE_ONCE(stopping_session_expiration_work, true);
|
| 184 | cancel_delayed_work_sync(&session_expiration_work);
|
| 185 | destroy_workqueue(ksmbd_conn_wq);
|
| 186 | ksmbd_conn_wq = NULL;
|
| 187 | }
|
| 188 | }
|
| 189 |
|
| 190 | |
| 191 | * __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup
|
| 192 | * of a ksmbd_conn whose refcount has just dropped to zero.
|
| 193 | *
|
| 194 | * This is the common release path used by ksmbd_conn_put() for the embedded
|
| 195 | * state that outlives the connection thread: async_ida and the attached
|
| 196 | * transport (which owns the socket and iov for TCP). Called from a workqueue
|
| 197 | * so that sleep-allowed teardown (sock_release -> tcp_close ->
|
| 198 | * lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu)
|
| 199 | * or any other non-sleeping putter context.
|
| 200 |
|
| 201 | static void __ksmbd_conn_release_work(struct work_struct *work)
|
| 202 | {
|
| 203 | struct ksmbd_conn *conn =
|
| 204 | container_of(work, struct ksmbd_conn, release_work);
|
| 205 |
|
| 206 | ida_destroy(&conn->async_ida);
|
| 207 | conn->transport->ops->free_transport(conn->transport);
|
| 208 | kfree_sensitive(conn);
|
| 209 | }
|
| 210 |
|
| 211 | |
| 212 | * ksmbd_conn_get() - take a reference on @conn and return it.
|
| 213 | *
|
| 214 | * @conn: connection instance to get a reference to
|
| 215 | *
|
| 216 | * Returns @conn unchanged so callers can write
|
| 217 | * "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL
|
| 218 | * if @conn is NULL.
|
| 219 |
|
| 220 | struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
|
| 221 | {
|
| 222 | if (!conn)
|
| 223 | return NULL;
|
| 224 |
|
| 225 | atomic_inc(&conn->refcnt);
|
| 226 | return conn;
|
| 227 | }
|
| 228 |
|
| 229 | |
| 230 | * ksmbd_conn_put() - drop a reference and, if it was the last, queue the
|
| 231 | * release onto ksmbd_conn_wq so it runs from process context.
|
| 232 | *
|
| 233 | * @conn: connection instance to put a reference to
|
| 234 | *
|
| 235 | * Callable from any context including RCU softirq callbacks and non-sleeping
|
| 236 | * locks; the actual release is deferred to the workqueue. ksmbd_conn_wq is
|
| 237 | * created in ksmbd_server_init() before any conn can be allocated and is
|
| 238 | * destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always
|
| 239 | * non-NULL while a conn reference is held.
|
| 240 |
|
| 241 | void ksmbd_conn_put(struct ksmbd_conn *conn)
|
| 242 | {
|
| 243 | if (!conn)
|
| 244 | return;
|
| 245 |
|
| 246 | if (atomic_dec_and_test(&conn->refcnt))
|
| 247 | queue_work(ksmbd_conn_wq, &conn->release_work);
|
| 248 | }
|
| 249 |
|
| 250 | |
| 251 | * ksmbd_conn_free() - free resources of the connection instance
|
| 252 | *
|
| 253 | * @conn: connection instance to be cleaned up
|
| 254 | *
|
| 255 | * During the thread termination, the corresponding conn instance
|
| 256 | * resources(sock/memory) are released and finally the conn object is freed.
|
| 257 |
|
| 258 | void ksmbd_conn_free(struct ksmbd_conn *conn)
|
| 259 | {
|
| 260 | down_write(&conn_list_lock);
|
| 261 | hash_del(&conn->hlist);
|
| 262 | up_write(&conn_list_lock);
|
| 263 |
|
| 264 | |
| 265 | * request_buf / preauth_info / mechToken are only ever accessed by the
|
| 266 | * connection handler thread that owns @conn. ksmbd_conn_free() is
|
| 267 | * called from the transport free_transport() path when that thread is
|
| 268 | * exiting, so it is safe to release them unconditionally even when
|
| 269 | * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file
|
| 270 | * holders only retain the conn pointer, not these per-thread buffers).
|
| 271 |
|
| 272 | xa_destroy(&conn->sessions);
|
| 273 | kvfree(conn->request_buf);
|
| 274 | kfree_sensitive(conn->preauth_info);
|
| 275 | kfree(conn->mechToken);
|
| 276 | ksmbd_preauth_session_destroy(conn);
|
| 277 | ksmbd_conn_put(conn);
|
| 278 | }
|
| 279 |
|
| 280 | |
| 281 | * ksmbd_conn_alloc() - initialize a new connection instance
|
| 282 | *
|
| 283 | * Return: ksmbd_conn struct on success, otherwise NULL
|
| 284 |
|
| 285 | struct ksmbd_conn *ksmbd_conn_alloc(void)
|
| 286 | {
|
| 287 | struct ksmbd_conn *conn;
|
| 288 |
|
| 289 | conn = kzalloc_obj(struct ksmbd_conn, KSMBD_DEFAULT_GFP);
|
| 290 | if (!conn)
|
| 291 | return NULL;
|
| 292 |
|
| 293 | conn->need_neg = true;
|
| 294 | conn->creation_time = jiffies;
|
| 295 | ksmbd_conn_set_new(conn);
|
| 296 | conn->local_nls = load_nls("utf8");
|
| 297 | if (!conn->local_nls)
|
| 298 | conn->local_nls = load_nls_default();
|
| 299 | if (IS_ENABLED(CONFIG_UNICODE))
|
| 300 | conn->um = utf8_load(UNICODE_AGE(12, 1, 0));
|
| 301 | else
|
| 302 | conn->um = ERR_PTR(-EOPNOTSUPP);
|
| 303 | if (IS_ERR(conn->um))
|
| 304 | conn->um = NULL;
|
| 305 | INIT_WORK(&conn->release_work, __ksmbd_conn_release_work);
|
| 306 | atomic_set(&conn->req_running, 0);
|
| 307 | atomic_set(&conn->r_count, 0);
|
| 308 | atomic_set(&conn->refcnt, 1);
|
| 309 | conn->total_credits = 1;
|
| 310 | conn->outstanding_credits = 0;
|
| 311 |
|
| 312 | |
| 313 | * The command sequence window starts as the set { 0 } when the
|
| 314 | * connection is established.
|
| 315 |
|
| 316 | conn->seq_low = 0;
|
| 317 | conn->seq_high = 1;
|
| 318 | __set_bit(0, conn->seq_bitmap);
|
| 319 |
|
| 320 | init_waitqueue_head(&conn->req_running_q);
|
| 321 | init_waitqueue_head(&conn->r_count_q);
|
| 322 | INIT_LIST_HEAD(&conn->requests);
|
| 323 | INIT_LIST_HEAD(&conn->async_requests);
|
| 324 | INIT_LIST_HEAD(&conn->preauth_sess_table);
|
| 325 | spin_lock_init(&conn->request_lock);
|
| 326 | spin_lock_init(&conn->credits_lock);
|
| 327 | ida_init(&conn->async_ida);
|
| 328 | xa_init(&conn->sessions);
|
| 329 |
|
| 330 | spin_lock_init(&conn->llist_lock);
|
| 331 | INIT_LIST_HEAD(&conn->lock_list);
|
| 332 |
|
| 333 | init_rwsem(&conn->session_lock);
|
| 334 |
|
| 335 | return conn;
|
| 336 | }
|
| 337 |
|
| 338 | bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
|
| 339 | {
|
| 340 | struct ksmbd_conn *t;
|
| 341 | int bkt;
|
| 342 | bool ret = false;
|
| 343 |
|
| 344 | down_read(&conn_list_lock);
|
| 345 | hash_for_each(conn_list, bkt, t, hlist) {
|
| 346 | if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
|
| 347 | continue;
|
| 348 |
|
| 349 | ret = true;
|
| 350 | break;
|
| 351 | }
|
| 352 | up_read(&conn_list_lock);
|
| 353 | return ret;
|
| 354 | }
|
| 355 |
|
| 356 | void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
|
| 357 | {
|
| 358 | struct ksmbd_conn *conn = work->conn;
|
| 359 | struct list_head *requests_queue = NULL;
|
| 360 |
|
| 361 | if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE)
|
| 362 | requests_queue = &conn->requests;
|
| 363 |
|
| 364 | atomic_inc(&conn->req_running);
|
| 365 | if (requests_queue) {
|
| 366 | spin_lock(&conn->request_lock);
|
| 367 | list_add_tail(&work->request_entry, requests_queue);
|
| 368 | spin_unlock(&conn->request_lock);
|
| 369 | }
|
| 370 | }
|
| 371 |
|
| 372 | void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
|
| 373 | {
|
| 374 | struct ksmbd_conn *conn = work->conn;
|
| 375 |
|
| 376 | atomic_dec(&conn->req_running);
|
| 377 | if (waitqueue_active(&conn->req_running_q))
|
| 378 | wake_up(&conn->req_running_q);
|
| 379 |
|
| 380 | if (list_empty(&work->request_entry) &&
|
| 381 | list_empty(&work->async_request_entry))
|
| 382 | return;
|
| 383 |
|
| 384 | spin_lock(&conn->request_lock);
|
| 385 | list_del_init(&work->request_entry);
|
| 386 | spin_unlock(&conn->request_lock);
|
| 387 | if (work->asynchronous)
|
| 388 | release_async_work(work);
|
| 389 |
|
| 390 | wake_up_all(&conn->req_running_q);
|
| 391 | }
|
| 392 |
|
| 393 | static void ksmbd_conn_cancel_async_requests(struct ksmbd_conn *conn)
|
| 394 | {
|
| 395 | struct ksmbd_work *work, *tmp;
|
| 396 |
|
| 397 | ksmbd_debug(CONN, "Cancel pending async requests on releasing connection\n");
|
| 398 | spin_lock(&conn->request_lock);
|
| 399 | list_for_each_entry_safe(work, tmp, &conn->async_requests,
|
| 400 | async_request_entry) {
|
| 401 | if (cmpxchg(&work->state, KSMBD_WORK_ACTIVE,
|
| 402 | KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
|
| 403 | continue;
|
| 404 |
|
| 405 | ksmbd_debug(CONN, "Cancel async request id %d\n",
|
| 406 | work->async_id);
|
| 407 | if (work->cancel_fn)
|
| 408 | work->cancel_fn(work->cancel_argv);
|
| 409 | }
|
| 410 | spin_unlock(&conn->request_lock);
|
| 411 | }
|
| 412 |
|
| 413 | void ksmbd_conn_lock(struct ksmbd_conn *conn)
|
| 414 | {
|
| 415 | mutex_lock(&conn->srv_mutex);
|
| 416 | }
|
| 417 |
|
| 418 | void ksmbd_conn_unlock(struct ksmbd_conn *conn)
|
| 419 | {
|
| 420 | mutex_unlock(&conn->srv_mutex);
|
| 421 | }
|
| 422 |
|
| 423 | static bool ksmbd_session_is_bound_to_conn(struct ksmbd_session *sess,
|
| 424 | struct ksmbd_conn *conn)
|
| 425 | {
|
| 426 | bool found;
|
| 427 |
|
| 428 | rcu_read_lock();
|
| 429 | found = xa_load(&conn->sessions, sess->id) == sess;
|
| 430 | rcu_read_unlock();
|
| 431 | if (found)
|
| 432 | return true;
|
| 433 |
|
| 434 | down_read(&sess->chann_lock);
|
| 435 | found = xa_load(&sess->ksmbd_chann_list, (long)conn);
|
| 436 | up_read(&sess->chann_lock);
|
| 437 | return found;
|
| 438 | }
|
| 439 |
|
| 440 | void ksmbd_all_conn_set_status(struct ksmbd_session *sess, u32 status)
|
| 441 | {
|
| 442 | struct ksmbd_conn *conn;
|
| 443 | int bkt;
|
| 444 |
|
| 445 | down_read(&conn_list_lock);
|
| 446 | hash_for_each(conn_list, bkt, conn, hlist) {
|
| 447 | if (ksmbd_session_is_bound_to_conn(sess, conn)) {
|
| 448 | spin_lock(&conn->request_lock);
|
| 449 | if (!ksmbd_conn_exiting(conn) &&
|
| 450 | !ksmbd_conn_releasing(conn))
|
| 451 | WRITE_ONCE(conn->status, status);
|
| 452 | spin_unlock(&conn->request_lock);
|
| 453 | }
|
| 454 | }
|
| 455 | up_read(&conn_list_lock);
|
| 456 | }
|
| 457 |
|
| 458 | void ksmbd_conn_abort(struct ksmbd_conn *conn)
|
| 459 | {
|
| 460 | bool shutdown = false;
|
| 461 |
|
| 462 | spin_lock(&conn->request_lock);
|
| 463 | if (!ksmbd_conn_exiting(conn) && !ksmbd_conn_releasing(conn)) {
|
| 464 | ksmbd_conn_set_exiting(conn);
|
| 465 | shutdown = true;
|
| 466 | }
|
| 467 | spin_unlock(&conn->request_lock);
|
| 468 | wake_up_all(&conn->req_running_q);
|
| 469 |
|
| 470 | if (shutdown && conn->transport->ops->shutdown)
|
| 471 | conn->transport->ops->shutdown(conn->transport);
|
| 472 | }
|
| 473 |
|
| 474 | void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
|
| 475 | {
|
| 476 | wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
|
| 477 | }
|
| 478 |
|
| 479 | int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
|
| 480 | struct ksmbd_session *sess)
|
| 481 | {
|
| 482 | struct ksmbd_conn *conn;
|
| 483 | int rc, retry_count = 0, max_timeout = 120;
|
| 484 | int rcount, bkt;
|
| 485 |
|
| 486 | retry_idle:
|
| 487 | if (retry_count >= max_timeout)
|
| 488 | return -EIO;
|
| 489 |
|
| 490 |
|
| 491 | ksmbd_wake_session_blocked_works(sess);
|
| 492 |
|
| 493 | down_read(&conn_list_lock);
|
| 494 | hash_for_each(conn_list, bkt, conn, hlist) {
|
| 495 | if (ksmbd_session_is_bound_to_conn(sess, conn)) {
|
| 496 | rcount = (conn == curr_conn) ? 2 : 1;
|
| 497 | if (atomic_read(&conn->req_running) >= rcount) {
|
| 498 | rc = wait_event_timeout(conn->req_running_q,
|
| 499 | atomic_read(&conn->req_running) < rcount,
|
| 500 | HZ);
|
| 501 | if (!rc) {
|
| 502 | up_read(&conn_list_lock);
|
| 503 | retry_count++;
|
| 504 | goto retry_idle;
|
| 505 | }
|
| 506 | }
|
| 507 | }
|
| 508 | }
|
| 509 | up_read(&conn_list_lock);
|
| 510 |
|
| 511 | return 0;
|
| 512 | }
|
| 513 |
|
| 514 | static int __ksmbd_conn_write(struct ksmbd_work *work,
|
| 515 | struct ksmbd_transport_write *tx)
|
| 516 | {
|
| 517 | struct ksmbd_conn *conn = work->conn;
|
| 518 | int sent;
|
| 519 |
|
| 520 | if (!work->response_buf) {
|
| 521 | pr_err("NULL response header\n");
|
| 522 | return -EINVAL;
|
| 523 | }
|
| 524 |
|
| 525 | if (work->send_no_response)
|
| 526 | return 0;
|
| 527 |
|
| 528 | if (!work->iov_idx)
|
| 529 | return -EINVAL;
|
| 530 |
|
| 531 | tx->iov = work->iov;
|
| 532 | tx->iov_cnt = work->iov_cnt;
|
| 533 | tx->size = get_rfc1002_len(work->iov[0].iov_base) + 4;
|
| 534 | tx->need_invalidate_rkey = work->need_invalidate_rkey;
|
| 535 | tx->remote_key = work->remote_key;
|
| 536 |
|
| 537 | ksmbd_conn_lock(conn);
|
| 538 | sent = conn->transport->ops->writev(conn->transport, tx);
|
| 539 | ksmbd_conn_unlock(conn);
|
| 540 |
|
| 541 | if (sent < 0) {
|
| 542 | pr_err("Failed to send message: %d\n", sent);
|
| 543 | return sent;
|
| 544 | }
|
| 545 |
|
| 546 | return 0;
|
| 547 | }
|
| 548 |
|
| 549 | int ksmbd_conn_write(struct ksmbd_work *work)
|
| 550 | {
|
| 551 | struct ksmbd_transport_write tx = {};
|
| 552 |
|
| 553 | return __ksmbd_conn_write(work, &tx);
|
| 554 | }
|
| 555 |
|
| 556 | int ksmbd_conn_write_eor(struct ksmbd_work *work)
|
| 557 | {
|
| 558 | struct ksmbd_transport_write tx = {
|
| 559 | .msg_flags = MSG_EOR,
|
| 560 | };
|
| 561 |
|
| 562 | return __ksmbd_conn_write(work, &tx);
|
| 563 | }
|
| 564 |
|
| 565 | int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
|
| 566 | void *buf, unsigned int buflen,
|
| 567 | struct smbdirect_buffer_descriptor_v1 *desc,
|
| 568 | unsigned int desc_len)
|
| 569 | {
|
| 570 | int ret = -EINVAL;
|
| 571 |
|
| 572 | if (conn->transport->ops->rdma_read)
|
| 573 | ret = conn->transport->ops->rdma_read(conn->transport,
|
| 574 | buf, buflen,
|
| 575 | desc, desc_len);
|
| 576 | return ret;
|
| 577 | }
|
| 578 |
|
| 579 | int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
|
| 580 | void *buf, unsigned int buflen,
|
| 581 | struct smbdirect_buffer_descriptor_v1 *desc,
|
| 582 | unsigned int desc_len)
|
| 583 | {
|
| 584 | int ret = -EINVAL;
|
| 585 |
|
| 586 | if (conn->transport->ops->rdma_write)
|
| 587 | ret = conn->transport->ops->rdma_write(conn->transport,
|
| 588 | buf, buflen,
|
| 589 | desc, desc_len);
|
| 590 | return ret;
|
| 591 | }
|
| 592 |
|
| 593 | bool ksmbd_conn_alive(struct ksmbd_conn *conn)
|
| 594 | {
|
| 595 | if (!ksmbd_server_running())
|
| 596 | return false;
|
| 597 |
|
| 598 | if (ksmbd_conn_exiting(conn))
|
| 599 | return false;
|
| 600 |
|
| 601 | if (kthread_should_stop())
|
| 602 | return false;
|
| 603 |
|
| 604 | |
| 605 | * Stale connections that have not completed NEGOTIATE and SESSION_SETUP
|
| 606 | * must be disconnected. Do not race a request that is currently
|
| 607 | * completing authentication.
|
| 608 |
|
| 609 | if (!atomic_read(&conn->req_running) &&
|
| 610 | time_after(jiffies, conn->creation_time +
|
| 611 | KSMBD_UNAUTHENTICATED_CONN_TIMEOUT) &&
|
| 612 | (READ_ONCE(conn->need_neg) ||
|
| 613 | !ksmbd_conn_has_valid_or_expired_session(conn))) {
|
| 614 | ksmbd_debug(CONN, "Connection setup timed out\n");
|
| 615 | return false;
|
| 616 | }
|
| 617 |
|
| 618 | if (atomic_read(&conn->stats.open_files_count) > 0)
|
| 619 | return true;
|
| 620 |
|
| 621 | |
| 622 | * Stop current session if the time that get last request from client
|
| 623 | * is bigger than deadtime user configured and opening file count is
|
| 624 | * zero.
|
| 625 |
|
| 626 | if (server_conf.deadtime > 0 &&
|
| 627 | time_after(jiffies, conn->last_active + server_conf.deadtime)) {
|
| 628 | ksmbd_debug(CONN, "No response from client in %lu minutes\n",
|
| 629 | server_conf.deadtime / SMB_ECHO_INTERVAL);
|
| 630 | return false;
|
| 631 | }
|
| 632 | return true;
|
| 633 | }
|
| 634 |
|
| 635 | static void ksmbd_session_expiration_worker(struct work_struct *work)
|
| 636 | {
|
| 637 | struct ksmbd_conn *conn, *target;
|
| 638 | int bkt;
|
| 639 |
|
| 640 | if (!ksmbd_server_running())
|
| 641 | goto reschedule;
|
| 642 |
|
| 643 | ksmbd_expire_sessions();
|
| 644 |
|
| 645 | |
| 646 | * An old connection without a Valid or Expired session must be
|
| 647 | * disconnected. Process one connection at a time without holding
|
| 648 | * conn_list_lock across transport shutdown.
|
| 649 |
|
| 650 | again:
|
| 651 | target = NULL;
|
| 652 | down_read(&conn_list_lock);
|
| 653 | hash_for_each(conn_list, bkt, conn, hlist) {
|
| 654 | if (ksmbd_conn_exiting(conn) || ksmbd_conn_releasing(conn) ||
|
| 655 | atomic_read(&conn->req_running) ||
|
| 656 | time_before_eq(jiffies, conn->creation_time +
|
| 657 | KSMBD_UNAUTHENTICATED_CONN_TIMEOUT) ||
|
| 658 | (!READ_ONCE(conn->need_neg) &&
|
| 659 | ksmbd_conn_has_valid_or_expired_session(conn)))
|
| 660 | continue;
|
| 661 |
|
| 662 | target = ksmbd_conn_get(conn);
|
| 663 | break;
|
| 664 | }
|
| 665 | up_read(&conn_list_lock);
|
| 666 |
|
| 667 | if (target) {
|
| 668 | ksmbd_debug(CONN, "Connection setup timed out\n");
|
| 669 | ksmbd_conn_abort(target);
|
| 670 | ksmbd_conn_put(target);
|
| 671 | goto again;
|
| 672 | }
|
| 673 |
|
| 674 | reschedule:
|
| 675 | if (!READ_ONCE(stopping_session_expiration_work))
|
| 676 | queue_delayed_work(ksmbd_conn_wq, &session_expiration_work,
|
| 677 | KSMBD_SESSION_EXPIRATION_INTERVAL);
|
| 678 | }
|
| 679 |
|
| 680 |
|
| 681 | #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
|
| 682 | #define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
|
| 683 | #define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE \
|
| 684 | (sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr))
|
| 685 |
|
| 686 | |
| 687 | * ksmbd_conn_handler_loop() - session thread to listen on new smb requests
|
| 688 | * @p: connection instance
|
| 689 | *
|
| 690 | * One thread each per connection
|
| 691 | *
|
| 692 | * Return: 0 on success
|
| 693 |
|
| 694 | int ksmbd_conn_handler_loop(void *p)
|
| 695 | {
|
| 696 | struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
|
| 697 | struct ksmbd_transport *t = conn->transport;
|
| 698 | unsigned int pdu_size, max_allowed_pdu_size, max_req;
|
| 699 | __le32 proto;
|
| 700 | char hdr_buf[4] = {0,};
|
| 701 | int size;
|
| 702 |
|
| 703 | mutex_init(&conn->srv_mutex);
|
| 704 | __module_get(THIS_MODULE);
|
| 705 |
|
| 706 | max_req = server_conf.max_inflight_req;
|
| 707 | conn->last_active = jiffies;
|
| 708 | set_freezable();
|
| 709 | while (ksmbd_conn_alive(conn)) {
|
| 710 | if (try_to_freeze())
|
| 711 | continue;
|
| 712 |
|
| 713 | kvfree(conn->request_buf);
|
| 714 | conn->request_buf = NULL;
|
| 715 |
|
| 716 | recheck:
|
| 717 | if (atomic_read(&conn->req_running) + 1 > max_req) {
|
| 718 | wait_event_interruptible(conn->req_running_q,
|
| 719 | atomic_read(&conn->req_running) < max_req);
|
| 720 | goto recheck;
|
| 721 | }
|
| 722 |
|
| 723 | size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1);
|
| 724 | if (size != sizeof(hdr_buf))
|
| 725 | break;
|
| 726 |
|
| 727 | pdu_size = get_rfc1002_len(hdr_buf);
|
| 728 | ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
|
| 729 |
|
| 730 | max_allowed_pdu_size = ksmbd_max_allowed_pdu_size(conn);
|
| 731 |
|
| 732 | if (pdu_size > max_allowed_pdu_size) {
|
| 733 | pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
|
| 734 | pdu_size, max_allowed_pdu_size,
|
| 735 | READ_ONCE(conn->status));
|
| 736 | break;
|
| 737 | }
|
| 738 |
|
| 739 | |
| 740 | * Check maximum pdu size(0x00FFFFFF).
|
| 741 |
|
| 742 | if (pdu_size > MAX_STREAM_PROT_LEN)
|
| 743 | break;
|
| 744 |
|
| 745 | if (pdu_size < SMB1_MIN_SUPPORTED_PDU_SIZE)
|
| 746 | break;
|
| 747 |
|
| 748 |
|
| 749 |
|
| 750 | size = pdu_size + 4 + 1;
|
| 751 | conn->request_buf = kvmalloc(size, KSMBD_DEFAULT_GFP);
|
| 752 | if (!conn->request_buf)
|
| 753 | break;
|
| 754 |
|
| 755 | memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf));
|
| 756 |
|
| 757 | |
| 758 | * We already read 4 bytes to find out PDU size, now
|
| 759 | * read in PDU
|
| 760 |
|
| 761 | size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2);
|
| 762 | if (size < 0) {
|
| 763 | pr_err("sock_read failed: %d\n", size);
|
| 764 | break;
|
| 765 | }
|
| 766 |
|
| 767 | if (size != pdu_size) {
|
| 768 | pr_err("PDU error. Read: %d, Expected: %d\n",
|
| 769 | size, pdu_size);
|
| 770 | continue;
|
| 771 | }
|
| 772 |
|
| 773 | if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
|
| 774 | SMB2_COMPRESSION_TRANSFORM_ID) {
|
| 775 | |
| 776 | * Convert the transform into a normal RFC1002-framed SMB2
|
| 777 | * request before protocol validation and work allocation.
|
| 778 |
|
| 779 | if (ksmbd_decompress_request(conn))
|
| 780 | break;
|
| 781 | pdu_size = get_rfc1002_len(conn->request_buf);
|
| 782 | }
|
| 783 |
|
| 784 | if (!ksmbd_smb_request(conn))
|
| 785 | break;
|
| 786 |
|
| 787 | proto = *(__le32 *)smb_get_msg(conn->request_buf);
|
| 788 | if (proto == SMB2_PROTO_NUMBER &&
|
| 789 | pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
|
| 790 | break;
|
| 791 |
|
| 792 | if (proto == SMB2_TRANSFORM_PROTO_NUM &&
|
| 793 | pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE)
|
| 794 | break;
|
| 795 |
|
| 796 | if (!default_conn_ops.process_fn) {
|
| 797 | pr_err("No connection request callback\n");
|
| 798 | break;
|
| 799 | }
|
| 800 |
|
| 801 | if (default_conn_ops.process_fn(conn)) {
|
| 802 | pr_err("Cannot handle request\n");
|
| 803 | break;
|
| 804 | }
|
| 805 | }
|
| 806 |
|
| 807 | ksmbd_conn_set_releasing(conn);
|
| 808 | ksmbd_conn_cancel_async_requests(conn);
|
| 809 |
|
| 810 | ksmbd_debug(CONN, "Wait for all pending requests(%d)\n", atomic_read(&conn->r_count));
|
| 811 | wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0);
|
| 812 |
|
| 813 | if (IS_ENABLED(CONFIG_UNICODE))
|
| 814 | utf8_unload(conn->um);
|
| 815 | unload_nls(conn->local_nls);
|
| 816 | if (default_conn_ops.terminate_fn)
|
| 817 | default_conn_ops.terminate_fn(conn);
|
| 818 | t->ops->disconnect(t);
|
| 819 | module_put(THIS_MODULE);
|
| 820 | return 0;
|
| 821 | }
|
| 822 |
|
| 823 | void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
|
| 824 | {
|
| 825 | default_conn_ops.process_fn = ops->process_fn;
|
| 826 | default_conn_ops.terminate_fn = ops->terminate_fn;
|
| 827 | }
|
| 828 |
|
| 829 | void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn)
|
| 830 | {
|
| 831 | atomic_inc(&conn->r_count);
|
| 832 | }
|
| 833 |
|
| 834 | void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
|
| 835 | {
|
| 836 | |
| 837 | * Checking waitqueue to dropping pending requests on
|
| 838 | * disconnection. waitqueue_active is safe because it
|
| 839 | * uses atomic operation for condition.
|
| 840 |
|
| 841 | atomic_inc(&conn->refcnt);
|
| 842 | if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
|
| 843 | wake_up(&conn->r_count_q);
|
| 844 |
|
| 845 | ksmbd_conn_put(conn);
|
| 846 | }
|
| 847 |
|
| 848 | int ksmbd_conn_transport_init(void)
|
| 849 | {
|
| 850 | int ret;
|
| 851 |
|
| 852 | mutex_lock(&init_lock);
|
| 853 | ret = ksmbd_tcp_init();
|
| 854 | if (ret) {
|
| 855 | pr_err("Failed to init TCP subsystem: %d\n", ret);
|
| 856 | goto out;
|
| 857 | }
|
| 858 |
|
| 859 | ret = ksmbd_rdma_init();
|
| 860 | if (ret) {
|
| 861 | pr_err("Failed to init RDMA subsystem: %d\n", ret);
|
| 862 | goto out;
|
| 863 | }
|
| 864 | out:
|
| 865 | mutex_unlock(&init_lock);
|
| 866 | if (create_proc_clients())
|
| 867 | pr_warn("Unable to create clients procfs entry\n");
|
| 868 | return ret;
|
| 869 | }
|
| 870 |
|
| 871 | static void stop_sessions(void)
|
| 872 | {
|
| 873 | struct ksmbd_conn *conn, *target;
|
| 874 | struct ksmbd_transport *t;
|
| 875 | bool any;
|
| 876 | int bkt;
|
| 877 |
|
| 878 | |
| 879 | * Serialised via init_lock; no concurrent stop_sessions() can
|
| 880 | * touch conn->stop_called, so writing it under the read lock is
|
| 881 | * safe.
|
| 882 |
|
| 883 | again:
|
| 884 | target = NULL;
|
| 885 | any = false;
|
| 886 | down_read(&conn_list_lock);
|
| 887 | hash_for_each(conn_list, bkt, conn, hlist) {
|
| 888 | any = true;
|
| 889 | if (conn->stop_called)
|
| 890 | continue;
|
| 891 | atomic_inc(&conn->refcnt);
|
| 892 | conn->stop_called = true;
|
| 893 | |
| 894 | * Mark the connection EXITING while still holding the
|
| 895 | * read lock so the selection and the status transition
|
| 896 | * happen together. Do not regress a connection that has
|
| 897 | * already advanced to RELEASING on its own (e.g. the
|
| 898 | * handler exited its receive loop for an unrelated
|
| 899 | * reason).
|
| 900 |
|
| 901 | spin_lock(&conn->request_lock);
|
| 902 | if (!ksmbd_conn_releasing(conn))
|
| 903 | ksmbd_conn_set_exiting(conn);
|
| 904 | spin_unlock(&conn->request_lock);
|
| 905 | target = conn;
|
| 906 | break;
|
| 907 | }
|
| 908 | up_read(&conn_list_lock);
|
| 909 |
|
| 910 | if (target) {
|
| 911 | t = target->transport;
|
| 912 | if (t->ops->shutdown)
|
| 913 | t->ops->shutdown(t);
|
| 914 | if (atomic_dec_and_test(&target->refcnt)) {
|
| 915 | ida_destroy(&target->async_ida);
|
| 916 | t->ops->free_transport(t);
|
| 917 | kfree_sensitive(target);
|
| 918 | }
|
| 919 | goto again;
|
| 920 | }
|
| 921 |
|
| 922 | if (any) {
|
| 923 | msleep(100);
|
| 924 | goto again;
|
| 925 | }
|
| 926 | }
|
| 927 |
|
| 928 | void ksmbd_conn_transport_destroy(void)
|
| 929 | {
|
| 930 | delete_proc_clients();
|
| 931 | mutex_lock(&init_lock);
|
| 932 | ksmbd_tcp_destroy();
|
| 933 | ksmbd_rdma_stop_listening();
|
| 934 | stop_sessions();
|
| 935 | mutex_unlock(&init_lock);
|
| 936 | }
|