1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/filesystem/inode.h" |
4 | |
5 | #include "mos/filesystem/page_cache.h" |
6 | #include "mos/filesystem/vfs_types.h" |
7 | #include "mos/mm/slab_autoinit.h" |
8 | #include "mos/syslog/printk.h" |
9 | |
10 | #include <mos/lib/structures/hashmap_common.h> |
11 | #include <mos_stdlib.h> |
12 | |
13 | slab_t *inode_cache; |
14 | SLAB_AUTOINIT("inode" , inode_cache, inode_t); |
15 | |
16 | static bool vfs_generic_inode_drop(inode_t *inode) |
17 | { |
18 | MOS_UNUSED(inode); |
19 | kfree(ptr: inode); |
20 | return true; |
21 | } |
22 | |
23 | static bool inode_try_drop(inode_t *inode) |
24 | { |
25 | if (inode->refcount == 0 && inode->nlinks == 0) |
26 | { |
27 | pr_dinfo2(vfs, "inode %p has 0 refcount and 0 nlinks, dropping" , (void *) inode); |
28 | |
29 | // drop the inode |
30 | mutex_acquire(mutex: &inode->cache.lock); |
31 | pagecache_flush_or_drop_all(icache: &inode->cache, drop_page: true); |
32 | mutex_release(mutex: &inode->cache.lock); |
33 | |
34 | bool dropped = false; |
35 | if (inode->superblock->ops && inode->superblock->ops->drop_inode) |
36 | dropped = inode->superblock->ops->drop_inode(inode); |
37 | else |
38 | dropped = vfs_generic_inode_drop(inode); |
39 | |
40 | if (!dropped) |
41 | pr_warn("inode %p has 0 refcount and 0 nlinks, but failed to be dropped" , (void *) inode); |
42 | |
43 | return dropped; |
44 | } |
45 | |
46 | return false; |
47 | } |
48 | |
49 | void inode_init(inode_t *inode, superblock_t *sb, u64 ino, file_type_t type) |
50 | { |
51 | inode->superblock = sb; |
52 | inode->ino = ino; |
53 | inode->type = type; |
54 | inode->file_ops = NULL; |
55 | inode->nlinks = 1; |
56 | inode->perm = 0; |
57 | inode->private_data = NULL; |
58 | inode->refcount = 0; |
59 | |
60 | hashmap_init(map: &inode->cache.pages, MOS_INODE_CACHE_HASHMAP_SIZE, hash_func: hashmap_identity_hash, compare_func: hashmap_simple_key_compare); |
61 | inode->cache.owner = inode; |
62 | inode->cache.lock = 0; |
63 | } |
64 | |
65 | inode_t *inode_create(superblock_t *sb, u64 ino, file_type_t type) |
66 | { |
67 | inode_t *inode = kmalloc(inode_cache); |
68 | inode_init(inode, sb, ino, type); |
69 | return inode; |
70 | } |
71 | |
72 | void inode_ref(inode_t *inode) |
73 | { |
74 | MOS_ASSERT(inode); |
75 | inode->refcount++; |
76 | } |
77 | |
78 | bool inode_unref(inode_t *inode) |
79 | { |
80 | MOS_ASSERT(inode); |
81 | MOS_ASSERT(inode->refcount > 0); |
82 | inode->refcount--; |
83 | return inode_try_drop(inode); |
84 | } |
85 | |
86 | bool inode_unlink(inode_t *dir, dentry_t *dentry) |
87 | { |
88 | inode_t *inode = dentry->inode; |
89 | MOS_ASSERT(dir && inode); |
90 | MOS_ASSERT(inode->nlinks > 0); |
91 | |
92 | inode->nlinks--; |
93 | bool ok = true; |
94 | if (dir->ops->unlink) |
95 | ok = dir->ops->unlink(dir, dentry); |
96 | |
97 | if (!ok) |
98 | { |
99 | inode->nlinks++; |
100 | return false; |
101 | } |
102 | |
103 | const bool dropped = inode_try_drop(inode: dentry->inode); |
104 | MOS_ASSERT_X(!dropped, "inode %p was dropped accidentally, where dentry %p should be holding a reference" , (void *) inode, (void *) dentry); |
105 | |
106 | return true; |
107 | } |
108 | |