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
13slab_t *inode_cache;
14SLAB_AUTOINIT("inode", inode_cache, inode_t);
15
16static bool vfs_generic_inode_drop(inode_t *inode)
17{
18 MOS_UNUSED(inode);
19 kfree(ptr: inode);
20 return true;
21}
22
23static 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
49void 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
65inode_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
72void inode_ref(inode_t *inode)
73{
74 MOS_ASSERT(inode);
75 inode->refcount++;
76}
77
78bool 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
86bool 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