1 /*
2  * Copyright (c) 2006-2025 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-05-05     Bernard      Implement vnode in dfs v2.0
9  */
10 
11 #include <dfs_file.h>
12 #include <dfs_mnt.h>
13 #ifdef RT_USING_PAGECACHE
14 #include "dfs_pcache.h"
15 #endif
16 
17 #define DBG_TAG    "DFS.vnode"
18 #define DBG_LVL    DBG_WARNING
19 #include <rtdbg.h>
20 
21 /**
22  * @brief Initialize a virtual node (vnode) structure
23  *
24  * @param[in,out] vnode Pointer to the vnode to be initialized
25  * @param[in] type Type of the vnode
26  * @param[in] fops Pointer to file operations structure
27  *
28  * @return int Always returns 0 indicating success
29  */
dfs_vnode_init(struct dfs_vnode * vnode,int type,const struct dfs_file_ops * fops)30 int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
31 {
32     if (vnode)
33     {
34         rt_memset(vnode, 0, sizeof(struct dfs_vnode));
35 
36         vnode->type = type;
37         rt_atomic_store(&(vnode->ref_count), 1);
38         vnode->mnt = RT_NULL;
39         vnode->fops = fops;
40     }
41 
42     return 0;
43 }
44 
45 /**
46  * @brief Create and initialize a new virtual node (vnode)
47  *
48  * @return struct dfs_vnode* Pointer to the newly created vnode, or NULL if creation failed
49  */
dfs_vnode_create(void)50 struct dfs_vnode *dfs_vnode_create(void)
51 {
52     struct dfs_vnode *vnode = rt_calloc(1, sizeof(struct dfs_vnode));
53     if (!vnode)
54     {
55         LOG_E("create a vnode failed.");
56         return RT_NULL;
57     }
58 
59     rt_atomic_store(&(vnode->ref_count), 1);
60 
61     LOG_I("create a vnode: %p", vnode);
62 
63     return vnode;
64 }
65 
66 /**
67  * @brief Destroy a virtual node (vnode) and free its resources
68  *
69  * @param[in] vnode Pointer to the vnode to be destroyed
70  *
71  * @return int Always returns 0. Note that this does not guarantee success, as errors may occur internally.
72  */
dfs_vnode_destroy(struct dfs_vnode * vnode)73 int dfs_vnode_destroy(struct dfs_vnode* vnode)
74 {
75     rt_err_t ret = RT_EOK;
76 
77     if (vnode)
78     {
79         ret = dfs_file_lock();
80         if (ret == RT_EOK)
81         {
82             if (rt_atomic_load(&(vnode->ref_count)) == 1)
83             {
84                 LOG_I("free a vnode: %p", vnode);
85 #ifdef RT_USING_PAGECACHE
86                 if (vnode->aspace)
87                 {
88                     dfs_aspace_destroy(vnode->aspace);
89                 }
90 #endif
91                 if (vnode->mnt)
92                 {
93                     DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
94                     vnode->mnt->fs_ops->free_vnode(vnode);
95                 }
96                 else
97                 {
98                     DLOG(msg, "vnode", "vnode", DLOG_MSG, "destroy vnode(mnt=NULL)");
99                 }
100 
101                 dfs_file_unlock();
102 
103                 rt_free(vnode);
104             }
105             else
106             {
107                 dfs_file_unlock();
108             }
109         }
110     }
111 
112     return 0;
113 }
114 
115 /**
116  * @brief Increase reference count of a virtual node (vnode)
117  *
118  * @param[in,out] vnode Pointer to the vnode to be referenced
119  *
120  * @return struct dfs_vnode* The same vnode pointer that was passed in
121  */
dfs_vnode_ref(struct dfs_vnode * vnode)122 struct dfs_vnode *dfs_vnode_ref(struct dfs_vnode *vnode)
123 {
124     if (vnode)
125     {
126         rt_atomic_add(&(vnode->ref_count), 1);
127 
128         DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
129     }
130 
131     return vnode;
132 }
133 
134 /**
135  * @brief Decrease reference count of a virtual node (vnode) and potentially free it
136  *
137  * @param[in,out] vnode Pointer to the vnode to be unreferenced
138  */
dfs_vnode_unref(struct dfs_vnode * vnode)139 void dfs_vnode_unref(struct dfs_vnode *vnode)
140 {
141     rt_err_t ret = RT_EOK;
142 
143     if (vnode)
144     {
145         ret = dfs_file_lock();
146         if (ret == RT_EOK)
147         {
148             rt_atomic_sub(&(vnode->ref_count), 1);
149             DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
150 #ifdef RT_USING_PAGECACHE
151             if (vnode->aspace)
152             {
153                 dfs_aspace_destroy(vnode->aspace);
154             }
155 #endif
156             if (rt_atomic_load(&(vnode->ref_count)) == 0)
157             {
158                 LOG_I("free a vnode: %p", vnode);
159                 DLOG(msg, "vnode", "vnode", DLOG_MSG, "free vnode, ref_count=0");
160 
161                 if (vnode->mnt)
162                 {
163                     DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
164                     vnode->mnt->fs_ops->free_vnode(vnode);
165                 }
166 
167                 dfs_file_unlock();
168 
169                 rt_free(vnode);
170             }
171             else
172             {
173                 dfs_file_unlock();
174                 DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
175             }
176         }
177     }
178 
179     return;
180 }
181