Commit | Line | Data |
---|---|---|
f4415848 NJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> | |
4 | * Copyright (C) 2019 Samsung Electronics Co., Ltd. | |
5 | */ | |
6 | ||
7 | #include <linux/fs.h> | |
5970e15d | 8 | #include <linux/filelock.h> |
f4415848 NJ |
9 | #include <linux/slab.h> |
10 | #include <linux/vmalloc.h> | |
11 | ||
f4415848 NJ |
12 | #include "glob.h" |
13 | #include "vfs_cache.h" | |
f4415848 NJ |
14 | #include "oplock.h" |
15 | #include "vfs.h" | |
16 | #include "connection.h" | |
17 | #include "mgmt/tree_connect.h" | |
18 | #include "mgmt/user_session.h" | |
f4415848 NJ |
19 | #include "smb_common.h" |
20 | ||
21 | #define S_DEL_PENDING 1 | |
22 | #define S_DEL_ON_CLS 2 | |
23 | #define S_DEL_ON_CLS_STREAM 8 | |
24 | ||
25 | static unsigned int inode_hash_mask __read_mostly; | |
26 | static unsigned int inode_hash_shift __read_mostly; | |
27 | static struct hlist_head *inode_hashtable __read_mostly; | |
28 | static DEFINE_RWLOCK(inode_hash_lock); | |
29 | ||
30 | static struct ksmbd_file_table global_ft; | |
31 | static atomic_long_t fd_limit; | |
c30f4eb8 | 32 | static struct kmem_cache *filp_cache; |
f4415848 NJ |
33 | |
34 | void ksmbd_set_fd_limit(unsigned long limit) | |
35 | { | |
36 | limit = min(limit, get_max_files()); | |
37 | atomic_long_set(&fd_limit, limit); | |
38 | } | |
39 | ||
40 | static bool fd_limit_depleted(void) | |
41 | { | |
42 | long v = atomic_long_dec_return(&fd_limit); | |
43 | ||
44 | if (v >= 0) | |
45 | return false; | |
46 | atomic_long_inc(&fd_limit); | |
47 | return true; | |
48 | } | |
49 | ||
50 | static void fd_limit_close(void) | |
51 | { | |
52 | atomic_long_inc(&fd_limit); | |
53 | } | |
54 | ||
55 | /* | |
56 | * INODE hash | |
57 | */ | |
58 | ||
59 | static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) | |
60 | { | |
61 | unsigned long tmp; | |
62 | ||
63 | tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / | |
64 | L1_CACHE_BYTES; | |
65 | tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); | |
66 | return tmp & inode_hash_mask; | |
67 | } | |
68 | ||
69 | static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) | |
70 | { | |
71 | struct hlist_head *head = inode_hashtable + | |
72 | inode_hash(inode->i_sb, inode->i_ino); | |
73 | struct ksmbd_inode *ci = NULL, *ret_ci = NULL; | |
74 | ||
75 | hlist_for_each_entry(ci, head, m_hash) { | |
76 | if (ci->m_inode == inode) { | |
77 | if (atomic_inc_not_zero(&ci->m_count)) | |
78 | ret_ci = ci; | |
79 | break; | |
80 | } | |
81 | } | |
82 | return ret_ci; | |
83 | } | |
84 | ||
85 | static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) | |
86 | { | |
ab0b263b | 87 | return __ksmbd_inode_lookup(file_inode(fp->filp)); |
f4415848 NJ |
88 | } |
89 | ||
90 | static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) | |
91 | { | |
92 | struct ksmbd_inode *ci; | |
93 | ||
94 | read_lock(&inode_hash_lock); | |
95 | ci = __ksmbd_inode_lookup(inode); | |
96 | read_unlock(&inode_hash_lock); | |
97 | return ci; | |
98 | } | |
99 | ||
100 | int ksmbd_query_inode_status(struct inode *inode) | |
101 | { | |
102 | struct ksmbd_inode *ci; | |
103 | int ret = KSMBD_INODE_STATUS_UNKNOWN; | |
104 | ||
105 | read_lock(&inode_hash_lock); | |
106 | ci = __ksmbd_inode_lookup(inode); | |
107 | if (ci) { | |
108 | ret = KSMBD_INODE_STATUS_OK; | |
109 | if (ci->m_flags & S_DEL_PENDING) | |
110 | ret = KSMBD_INODE_STATUS_PENDING_DELETE; | |
111 | atomic_dec(&ci->m_count); | |
112 | } | |
113 | read_unlock(&inode_hash_lock); | |
114 | return ret; | |
115 | } | |
116 | ||
117 | bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) | |
118 | { | |
119 | return (fp->f_ci->m_flags & S_DEL_PENDING); | |
120 | } | |
121 | ||
122 | void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) | |
123 | { | |
124 | fp->f_ci->m_flags |= S_DEL_PENDING; | |
125 | } | |
126 | ||
127 | void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) | |
128 | { | |
129 | fp->f_ci->m_flags &= ~S_DEL_PENDING; | |
130 | } | |
131 | ||
132 | void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, | |
133 | int file_info) | |
134 | { | |
135 | if (ksmbd_stream_fd(fp)) { | |
136 | fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; | |
137 | return; | |
138 | } | |
139 | ||
140 | fp->f_ci->m_flags |= S_DEL_ON_CLS; | |
141 | } | |
142 | ||
143 | static void ksmbd_inode_hash(struct ksmbd_inode *ci) | |
144 | { | |
145 | struct hlist_head *b = inode_hashtable + | |
146 | inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); | |
147 | ||
148 | hlist_add_head(&ci->m_hash, b); | |
149 | } | |
150 | ||
151 | static void ksmbd_inode_unhash(struct ksmbd_inode *ci) | |
152 | { | |
153 | write_lock(&inode_hash_lock); | |
154 | hlist_del_init(&ci->m_hash); | |
155 | write_unlock(&inode_hash_lock); | |
156 | } | |
157 | ||
158 | static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) | |
159 | { | |
ab0b263b | 160 | ci->m_inode = file_inode(fp->filp); |
f4415848 NJ |
161 | atomic_set(&ci->m_count, 1); |
162 | atomic_set(&ci->op_count, 0); | |
163 | atomic_set(&ci->sop_count, 0); | |
164 | ci->m_flags = 0; | |
165 | ci->m_fattr = 0; | |
166 | INIT_LIST_HEAD(&ci->m_fp_list); | |
167 | INIT_LIST_HEAD(&ci->m_op_list); | |
168 | rwlock_init(&ci->m_lock); | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) | |
173 | { | |
174 | struct ksmbd_inode *ci, *tmpci; | |
175 | int rc; | |
176 | ||
177 | read_lock(&inode_hash_lock); | |
178 | ci = ksmbd_inode_lookup(fp); | |
179 | read_unlock(&inode_hash_lock); | |
180 | if (ci) | |
181 | return ci; | |
182 | ||
183 | ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); | |
184 | if (!ci) | |
185 | return NULL; | |
186 | ||
187 | rc = ksmbd_inode_init(ci, fp); | |
188 | if (rc) { | |
bde1694a | 189 | pr_err("inode initialized failed\n"); |
f4415848 NJ |
190 | kfree(ci); |
191 | return NULL; | |
192 | } | |
193 | ||
194 | write_lock(&inode_hash_lock); | |
195 | tmpci = ksmbd_inode_lookup(fp); | |
196 | if (!tmpci) { | |
197 | ksmbd_inode_hash(ci); | |
198 | } else { | |
199 | kfree(ci); | |
200 | ci = tmpci; | |
201 | } | |
202 | write_unlock(&inode_hash_lock); | |
203 | return ci; | |
204 | } | |
205 | ||
206 | static void ksmbd_inode_free(struct ksmbd_inode *ci) | |
207 | { | |
208 | ksmbd_inode_unhash(ci); | |
209 | kfree(ci); | |
210 | } | |
211 | ||
212 | static void ksmbd_inode_put(struct ksmbd_inode *ci) | |
213 | { | |
214 | if (atomic_dec_and_test(&ci->m_count)) | |
215 | ksmbd_inode_free(ci); | |
216 | } | |
217 | ||
218 | int __init ksmbd_inode_hash_init(void) | |
219 | { | |
220 | unsigned int loop; | |
221 | unsigned long numentries = 16384; | |
222 | unsigned long bucketsize = sizeof(struct hlist_head); | |
223 | unsigned long size; | |
224 | ||
225 | inode_hash_shift = ilog2(numentries); | |
226 | inode_hash_mask = (1 << inode_hash_shift) - 1; | |
227 | ||
228 | size = bucketsize << inode_hash_shift; | |
229 | ||
230 | /* init master fp hash table */ | |
231 | inode_hashtable = vmalloc(size); | |
232 | if (!inode_hashtable) | |
233 | return -ENOMEM; | |
234 | ||
235 | for (loop = 0; loop < (1U << inode_hash_shift); loop++) | |
236 | INIT_HLIST_HEAD(&inode_hashtable[loop]); | |
237 | return 0; | |
238 | } | |
239 | ||
849f59e1 | 240 | void ksmbd_release_inode_hash(void) |
f4415848 NJ |
241 | { |
242 | vfree(inode_hashtable); | |
243 | } | |
244 | ||
245 | static void __ksmbd_inode_close(struct ksmbd_file *fp) | |
246 | { | |
f4415848 NJ |
247 | struct ksmbd_inode *ci = fp->f_ci; |
248 | int err; | |
249 | struct file *filp; | |
250 | ||
251 | filp = fp->filp; | |
252 | if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { | |
253 | ci->m_flags &= ~S_DEL_ON_CLS_STREAM; | |
4609e1f1 | 254 | err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp), |
af34983e | 255 | filp->f_path.dentry, |
f4415848 NJ |
256 | fp->stream.name); |
257 | if (err) | |
bde1694a NJ |
258 | pr_err("remove xattr failed : %s\n", |
259 | fp->stream.name); | |
f4415848 NJ |
260 | } |
261 | ||
262 | if (atomic_dec_and_test(&ci->m_count)) { | |
263 | write_lock(&ci->m_lock); | |
264 | if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { | |
f4415848 NJ |
265 | ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); |
266 | write_unlock(&ci->m_lock); | |
74d7970f | 267 | ksmbd_vfs_unlink(filp); |
f4415848 NJ |
268 | write_lock(&ci->m_lock); |
269 | } | |
270 | write_unlock(&ci->m_lock); | |
271 | ||
272 | ksmbd_inode_free(ci); | |
273 | } | |
274 | } | |
275 | ||
276 | static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) | |
277 | { | |
3867369e | 278 | if (!has_file_id(fp->persistent_id)) |
f4415848 NJ |
279 | return; |
280 | ||
281 | write_lock(&global_ft.lock); | |
282 | idr_remove(global_ft.idr, fp->persistent_id); | |
283 | write_unlock(&global_ft.lock); | |
284 | } | |
285 | ||
64b39f4a | 286 | static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) |
f4415848 | 287 | { |
3867369e | 288 | if (!has_file_id(fp->volatile_id)) |
f4415848 NJ |
289 | return; |
290 | ||
291 | write_lock(&fp->f_ci->m_lock); | |
292 | list_del_init(&fp->node); | |
293 | write_unlock(&fp->f_ci->m_lock); | |
294 | ||
295 | write_lock(&ft->lock); | |
296 | idr_remove(ft->idr, fp->volatile_id); | |
297 | write_unlock(&ft->lock); | |
298 | } | |
299 | ||
64b39f4a | 300 | static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) |
f4415848 NJ |
301 | { |
302 | struct file *filp; | |
d63528eb | 303 | struct ksmbd_lock *smb_lock, *tmp_lock; |
f4415848 NJ |
304 | |
305 | fd_limit_close(); | |
306 | __ksmbd_remove_durable_fd(fp); | |
307 | __ksmbd_remove_fd(ft, fp); | |
308 | ||
309 | close_id_del_oplock(fp); | |
310 | filp = fp->filp; | |
311 | ||
312 | __ksmbd_inode_close(fp); | |
313 | if (!IS_ERR_OR_NULL(filp)) | |
314 | fput(filp); | |
d63528eb HL |
315 | |
316 | /* because the reference count of fp is 0, it is guaranteed that | |
317 | * there are not accesses to fp->lock_list. | |
318 | */ | |
319 | list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { | |
320 | spin_lock(&fp->conn->llist_lock); | |
321 | list_del(&smb_lock->clist); | |
322 | spin_unlock(&fp->conn->llist_lock); | |
323 | ||
324 | list_del(&smb_lock->flist); | |
325 | locks_free_lock(smb_lock->fl); | |
326 | kfree(smb_lock); | |
327 | } | |
328 | ||
f4415848 NJ |
329 | if (ksmbd_stream_fd(fp)) |
330 | kfree(fp->stream.name); | |
c30f4eb8 | 331 | kmem_cache_free(filp_cache, fp); |
f4415848 NJ |
332 | } |
333 | ||
334 | static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) | |
335 | { | |
336 | if (!atomic_inc_not_zero(&fp->refcount)) | |
337 | return NULL; | |
338 | return fp; | |
339 | } | |
340 | ||
341 | static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, | |
3867369e | 342 | u64 id) |
f4415848 | 343 | { |
f4415848 NJ |
344 | struct ksmbd_file *fp; |
345 | ||
3867369e NJ |
346 | if (!has_file_id(id)) |
347 | return NULL; | |
348 | ||
f4415848 NJ |
349 | read_lock(&ft->lock); |
350 | fp = idr_find(ft->idr, id); | |
351 | if (fp) | |
352 | fp = ksmbd_fp_get(fp); | |
f4415848 | 353 | read_unlock(&ft->lock); |
f4415848 NJ |
354 | return fp; |
355 | } | |
356 | ||
64b39f4a | 357 | static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) |
f4415848 NJ |
358 | { |
359 | __ksmbd_close_fd(&work->sess->file_table, fp); | |
360 | atomic_dec(&work->conn->stats.open_files_count); | |
361 | } | |
362 | ||
363 | static void set_close_state_blocked_works(struct ksmbd_file *fp) | |
364 | { | |
d3ca9f7a | 365 | struct ksmbd_work *cancel_work; |
f4415848 NJ |
366 | |
367 | spin_lock(&fp->f_lock); | |
d3ca9f7a | 368 | list_for_each_entry(cancel_work, &fp->blocked_works, |
070fb21e | 369 | fp_entry) { |
f4415848 NJ |
370 | cancel_work->state = KSMBD_WORK_CLOSED; |
371 | cancel_work->cancel_fn(cancel_work->cancel_argv); | |
372 | } | |
373 | spin_unlock(&fp->f_lock); | |
374 | } | |
375 | ||
3867369e | 376 | int ksmbd_close_fd(struct ksmbd_work *work, u64 id) |
f4415848 NJ |
377 | { |
378 | struct ksmbd_file *fp; | |
379 | struct ksmbd_file_table *ft; | |
380 | ||
3867369e | 381 | if (!has_file_id(id)) |
f4415848 NJ |
382 | return 0; |
383 | ||
384 | ft = &work->sess->file_table; | |
385 | read_lock(&ft->lock); | |
386 | fp = idr_find(ft->idr, id); | |
387 | if (fp) { | |
388 | set_close_state_blocked_works(fp); | |
389 | ||
390 | if (!atomic_dec_and_test(&fp->refcount)) | |
391 | fp = NULL; | |
392 | } | |
393 | read_unlock(&ft->lock); | |
394 | ||
395 | if (!fp) | |
396 | return -EINVAL; | |
397 | ||
398 | __put_fd_final(work, fp); | |
399 | return 0; | |
400 | } | |
401 | ||
64b39f4a | 402 | void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) |
f4415848 NJ |
403 | { |
404 | if (!fp) | |
405 | return; | |
406 | ||
407 | if (!atomic_dec_and_test(&fp->refcount)) | |
408 | return; | |
409 | __put_fd_final(work, fp); | |
410 | } | |
411 | ||
64b39f4a | 412 | static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) |
f4415848 NJ |
413 | { |
414 | if (!fp) | |
415 | return false; | |
416 | if (fp->tcon != tcon) | |
417 | return false; | |
418 | return true; | |
419 | } | |
420 | ||
3867369e | 421 | struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) |
f4415848 NJ |
422 | { |
423 | return __ksmbd_lookup_fd(&work->sess->file_table, id); | |
424 | } | |
425 | ||
3867369e | 426 | struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) |
f4415848 NJ |
427 | { |
428 | struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); | |
429 | ||
430 | if (__sanity_check(work->tcon, fp)) | |
431 | return fp; | |
432 | ||
433 | ksmbd_fd_put(work, fp); | |
434 | return NULL; | |
435 | } | |
436 | ||
3867369e NJ |
437 | struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, |
438 | u64 pid) | |
f4415848 NJ |
439 | { |
440 | struct ksmbd_file *fp; | |
441 | ||
3867369e | 442 | if (!has_file_id(id)) { |
f4415848 NJ |
443 | id = work->compound_fid; |
444 | pid = work->compound_pfid; | |
445 | } | |
446 | ||
f4415848 NJ |
447 | fp = __ksmbd_lookup_fd(&work->sess->file_table, id); |
448 | if (!__sanity_check(work->tcon, fp)) { | |
449 | ksmbd_fd_put(work, fp); | |
450 | return NULL; | |
451 | } | |
452 | if (fp->persistent_id != pid) { | |
453 | ksmbd_fd_put(work, fp); | |
454 | return NULL; | |
455 | } | |
456 | return fp; | |
457 | } | |
458 | ||
459 | struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) | |
460 | { | |
461 | return __ksmbd_lookup_fd(&global_ft, id); | |
462 | } | |
463 | ||
f4415848 NJ |
464 | struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) |
465 | { | |
466 | struct ksmbd_file *fp = NULL; | |
467 | unsigned int id; | |
468 | ||
469 | read_lock(&global_ft.lock); | |
470 | idr_for_each_entry(global_ft.idr, fp, id) { | |
471 | if (!memcmp(fp->create_guid, | |
472 | cguid, | |
473 | SMB2_CREATE_GUID_SIZE)) { | |
474 | fp = ksmbd_fp_get(fp); | |
475 | break; | |
476 | } | |
477 | } | |
478 | read_unlock(&global_ft.lock); | |
479 | ||
480 | return fp; | |
481 | } | |
482 | ||
f4415848 NJ |
483 | struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) |
484 | { | |
485 | struct ksmbd_file *lfp; | |
486 | struct ksmbd_inode *ci; | |
f4415848 NJ |
487 | |
488 | ci = ksmbd_inode_lookup_by_vfsinode(inode); | |
489 | if (!ci) | |
490 | return NULL; | |
491 | ||
492 | read_lock(&ci->m_lock); | |
6f3d5eee | 493 | list_for_each_entry(lfp, &ci->m_fp_list, node) { |
ab0b263b | 494 | if (inode == file_inode(lfp->filp)) { |
f4415848 | 495 | atomic_dec(&ci->m_count); |
8510a043 | 496 | lfp = ksmbd_fp_get(lfp); |
f4415848 NJ |
497 | read_unlock(&ci->m_lock); |
498 | return lfp; | |
499 | } | |
500 | } | |
501 | atomic_dec(&ci->m_count); | |
502 | read_unlock(&ci->m_lock); | |
503 | return NULL; | |
504 | } | |
505 | ||
506 | #define OPEN_ID_TYPE_VOLATILE_ID (0) | |
507 | #define OPEN_ID_TYPE_PERSISTENT_ID (1) | |
508 | ||
3867369e | 509 | static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) |
f4415848 NJ |
510 | { |
511 | if (type == OPEN_ID_TYPE_VOLATILE_ID) | |
512 | fp->volatile_id = id; | |
513 | if (type == OPEN_ID_TYPE_PERSISTENT_ID) | |
514 | fp->persistent_id = id; | |
515 | } | |
516 | ||
64b39f4a | 517 | static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, |
f4415848 NJ |
518 | int type) |
519 | { | |
3867369e | 520 | u64 id = 0; |
f4415848 NJ |
521 | int ret; |
522 | ||
523 | if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { | |
524 | __open_id_set(fp, KSMBD_NO_FID, type); | |
525 | return -EMFILE; | |
526 | } | |
527 | ||
528 | idr_preload(GFP_KERNEL); | |
529 | write_lock(&ft->lock); | |
3867369e | 530 | ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); |
f4415848 NJ |
531 | if (ret >= 0) { |
532 | id = ret; | |
533 | ret = 0; | |
534 | } else { | |
535 | id = KSMBD_NO_FID; | |
536 | fd_limit_close(); | |
537 | } | |
538 | ||
539 | __open_id_set(fp, id, type); | |
540 | write_unlock(&ft->lock); | |
541 | idr_preload_end(); | |
542 | return ret; | |
543 | } | |
544 | ||
545 | unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) | |
546 | { | |
547 | __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); | |
548 | return fp->persistent_id; | |
549 | } | |
550 | ||
64b39f4a | 551 | struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) |
f4415848 | 552 | { |
c30f4eb8 | 553 | struct ksmbd_file *fp; |
f4415848 NJ |
554 | int ret; |
555 | ||
c30f4eb8 | 556 | fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); |
f4415848 | 557 | if (!fp) { |
bde1694a | 558 | pr_err("Failed to allocate memory\n"); |
f4415848 NJ |
559 | return ERR_PTR(-ENOMEM); |
560 | } | |
561 | ||
562 | INIT_LIST_HEAD(&fp->blocked_works); | |
563 | INIT_LIST_HEAD(&fp->node); | |
d63528eb | 564 | INIT_LIST_HEAD(&fp->lock_list); |
f4415848 NJ |
565 | spin_lock_init(&fp->f_lock); |
566 | atomic_set(&fp->refcount, 1); | |
567 | ||
568 | fp->filp = filp; | |
af7c39d9 | 569 | fp->conn = work->conn; |
f4415848 NJ |
570 | fp->tcon = work->tcon; |
571 | fp->volatile_id = KSMBD_NO_FID; | |
572 | fp->persistent_id = KSMBD_NO_FID; | |
573 | fp->f_ci = ksmbd_inode_get(fp); | |
574 | ||
575 | if (!fp->f_ci) { | |
1dfb8242 NJ |
576 | ret = -ENOMEM; |
577 | goto err_out; | |
f4415848 NJ |
578 | } |
579 | ||
580 | ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); | |
581 | if (ret) { | |
582 | ksmbd_inode_put(fp->f_ci); | |
1dfb8242 | 583 | goto err_out; |
f4415848 NJ |
584 | } |
585 | ||
586 | atomic_inc(&work->conn->stats.open_files_count); | |
587 | return fp; | |
1dfb8242 NJ |
588 | |
589 | err_out: | |
590 | kmem_cache_free(filp_cache, fp); | |
591 | return ERR_PTR(ret); | |
f4415848 NJ |
592 | } |
593 | ||
f4415848 | 594 | static int |
070fb21e NJ |
595 | __close_file_table_ids(struct ksmbd_file_table *ft, |
596 | struct ksmbd_tree_connect *tcon, | |
597 | bool (*skip)(struct ksmbd_tree_connect *tcon, | |
598 | struct ksmbd_file *fp)) | |
f4415848 NJ |
599 | { |
600 | unsigned int id; | |
601 | struct ksmbd_file *fp; | |
602 | int num = 0; | |
603 | ||
604 | idr_for_each_entry(ft->idr, fp, id) { | |
605 | if (skip(tcon, fp)) | |
606 | continue; | |
607 | ||
608 | set_close_state_blocked_works(fp); | |
609 | ||
610 | if (!atomic_dec_and_test(&fp->refcount)) | |
611 | continue; | |
612 | __ksmbd_close_fd(ft, fp); | |
613 | num++; | |
614 | } | |
615 | return num; | |
616 | } | |
617 | ||
79a8a71d NJ |
618 | static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, |
619 | struct ksmbd_file *fp) | |
f4415848 NJ |
620 | { |
621 | return fp->tcon != tcon; | |
622 | } | |
623 | ||
79a8a71d NJ |
624 | static bool session_fd_check(struct ksmbd_tree_connect *tcon, |
625 | struct ksmbd_file *fp) | |
f4415848 | 626 | { |
73f9dad5 | 627 | return false; |
f4415848 NJ |
628 | } |
629 | ||
630 | void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) | |
631 | { | |
632 | int num = __close_file_table_ids(&work->sess->file_table, | |
633 | work->tcon, | |
634 | tree_conn_fd_check); | |
635 | ||
636 | atomic_sub(num, &work->conn->stats.open_files_count); | |
637 | } | |
638 | ||
639 | void ksmbd_close_session_fds(struct ksmbd_work *work) | |
640 | { | |
641 | int num = __close_file_table_ids(&work->sess->file_table, | |
642 | work->tcon, | |
643 | session_fd_check); | |
644 | ||
645 | atomic_sub(num, &work->conn->stats.open_files_count); | |
646 | } | |
647 | ||
648 | int ksmbd_init_global_file_table(void) | |
649 | { | |
650 | return ksmbd_init_file_table(&global_ft); | |
651 | } | |
652 | ||
653 | void ksmbd_free_global_file_table(void) | |
654 | { | |
655 | struct ksmbd_file *fp = NULL; | |
656 | unsigned int id; | |
657 | ||
658 | idr_for_each_entry(global_ft.idr, fp, id) { | |
659 | __ksmbd_remove_durable_fd(fp); | |
c30f4eb8 | 660 | kmem_cache_free(filp_cache, fp); |
f4415848 NJ |
661 | } |
662 | ||
663 | ksmbd_destroy_file_table(&global_ft); | |
664 | } | |
665 | ||
f4415848 NJ |
666 | int ksmbd_init_file_table(struct ksmbd_file_table *ft) |
667 | { | |
20ea7fd2 | 668 | ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); |
f4415848 NJ |
669 | if (!ft->idr) |
670 | return -ENOMEM; | |
671 | ||
672 | idr_init(ft->idr); | |
673 | rwlock_init(&ft->lock); | |
674 | return 0; | |
675 | } | |
676 | ||
677 | void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) | |
678 | { | |
679 | if (!ft->idr) | |
680 | return; | |
681 | ||
682 | __close_file_table_ids(ft, NULL, session_fd_check); | |
683 | idr_destroy(ft->idr); | |
822bc8ea | 684 | kfree(ft->idr); |
f4415848 NJ |
685 | ft->idr = NULL; |
686 | } | |
c30f4eb8 NJ |
687 | |
688 | int ksmbd_init_file_cache(void) | |
689 | { | |
690 | filp_cache = kmem_cache_create("ksmbd_file_cache", | |
691 | sizeof(struct ksmbd_file), 0, | |
692 | SLAB_HWCACHE_ALIGN, NULL); | |
693 | if (!filp_cache) | |
694 | goto out; | |
695 | ||
696 | return 0; | |
697 | ||
698 | out: | |
bde1694a | 699 | pr_err("failed to allocate file cache\n"); |
c30f4eb8 NJ |
700 | return -ENOMEM; |
701 | } | |
702 | ||
703 | void ksmbd_exit_file_cache(void) | |
704 | { | |
705 | kmem_cache_destroy(filp_cache); | |
706 | } |