summaryrefslogtreecommitdiff
path: root/components/dfs/dfs_v2/src/dfs_vnode.c
blob: 462f94e05e68f2fe53ac224c327a9846a0c85ec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
 * Copyright (c) 2006-2025 RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-05-05     Bernard      Implement vnode in dfs v2.0
 */

#include <dfs_file.h>
#include <dfs_dentry.h>
#include <dfs_mnt.h>
#ifdef RT_USING_PAGECACHE
#include "dfs_pcache.h"
#endif

#define DBG_TAG    "DFS.vnode"
#define DBG_LVL    DBG_WARNING
#include <rtdbg.h>

/**
 * @brief Initialize a virtual node (vnode) structure
 *
 * @param[in,out] vnode Pointer to the vnode to be initialized
 * @param[in] type Type of the vnode
 * @param[in] fops Pointer to file operations structure
 *
 * @return int Always returns 0 indicating success
 */
int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
{
    if (vnode)
    {
        rt_memset(vnode, 0, sizeof(struct dfs_vnode));

        vnode->type = type;
        rt_atomic_store(&(vnode->ref_count), 1);
        vnode->mnt = RT_NULL;
        vnode->fops = fops;
    }

    return 0;
}

/**
 * @brief Create and initialize a new virtual node (vnode)
 *
 * @return struct dfs_vnode* Pointer to the newly created vnode, or NULL if creation failed
 */
struct dfs_vnode *dfs_vnode_create(void)
{
    struct dfs_vnode *vnode = rt_calloc(1, sizeof(struct dfs_vnode));
    if (!vnode)
    {
        LOG_E("create a vnode failed.");
        return RT_NULL;
    }

    rt_atomic_store(&(vnode->ref_count), 1);

    LOG_I("create a vnode: %p", vnode);

    return vnode;
}

/**
 * @brief Initialize the lock of a virtual node (vnode)
 *
 * @param[in,out] vnode Pointer to the vnode containing the lock
 * @param[in] dentry Pointer to the dentry used to identify the vnode
 *
 * @return RT_EOK on success, or -RT_EINVAL if a parameter is invalid
 */
rt_err_t dfs_vnode_lock_init(struct dfs_vnode *vnode, struct dfs_dentry *dentry)
{
    char lock_name[RT_NAME_MAX];
    uint32_t path_hash;

    if (vnode == RT_NULL || dentry == RT_NULL)
    {
        return -RT_EINVAL;
    }

    path_hash = dfs_dentry_full_path_crc32(dentry);
    rt_snprintf(lock_name, sizeof(lock_name), "vn-%04x",
                (unsigned int)(path_hash & 0xffffU));

    return rt_mutex_init(&vnode->lock, lock_name, RT_IPC_FLAG_PRIO);
}

/**
 * @brief Destroy a virtual node (vnode) and free its resources
 *
 * @param[in] vnode Pointer to the vnode to be destroyed
 *
 * @return int Always returns 0. Note that this does not guarantee success, as errors may occur internally.
 */
int dfs_vnode_destroy(struct dfs_vnode* vnode)
{
    rt_err_t ret = RT_EOK;

    if (vnode)
    {
        ret = dfs_file_lock();
        if (ret == RT_EOK)
        {
            if (rt_atomic_load(&(vnode->ref_count)) == 1)
            {
                LOG_I("free a vnode: %p", vnode);
#ifdef RT_USING_PAGECACHE
                if (vnode->aspace)
                {
                    dfs_aspace_destroy(vnode->aspace);
                }
#endif
                if (vnode->mnt)
                {
                    DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
                    vnode->mnt->fs_ops->free_vnode(vnode);
                }
                else
                {
                    DLOG(msg, "vnode", "vnode", DLOG_MSG, "destroy vnode(mnt=NULL)");
                }

                dfs_file_unlock();

                rt_free(vnode);
            }
            else
            {
                dfs_file_unlock();
            }
        }
    }

    return 0;
}

/**
 * @brief Increase reference count of a virtual node (vnode)
 *
 * @param[in,out] vnode Pointer to the vnode to be referenced
 *
 * @return struct dfs_vnode* The same vnode pointer that was passed in
 */
struct dfs_vnode *dfs_vnode_ref(struct dfs_vnode *vnode)
{
    if (vnode)
    {
        rt_atomic_add(&(vnode->ref_count), 1);

        DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
    }

    return vnode;
}

/**
 * @brief Decrease reference count of a virtual node (vnode) and potentially free it
 *
 * @param[in,out] vnode Pointer to the vnode to be unreferenced
 */
void dfs_vnode_unref(struct dfs_vnode *vnode)
{
    rt_err_t ret = RT_EOK;

    if (vnode)
    {
        ret = dfs_file_lock();
        if (ret == RT_EOK)
        {
            rt_atomic_sub(&(vnode->ref_count), 1);
            DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
#ifdef RT_USING_PAGECACHE
            if (vnode->aspace)
            {
                dfs_aspace_destroy(vnode->aspace);
            }
#endif
            if (rt_atomic_load(&(vnode->ref_count)) == 0)
            {
                LOG_I("free a vnode: %p", vnode);
                DLOG(msg, "vnode", "vnode", DLOG_MSG, "free vnode, ref_count=0");

                if (vnode->mnt)
                {
                    DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
                    vnode->mnt->fs_ops->free_vnode(vnode);
                }

                dfs_file_unlock();

                rt_free(vnode);
            }
            else
            {
                dfs_file_unlock();
                DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
            }
        }
    }

    return;
}