source: mainline/uspace/srv/vfs/vfs_node.c@ b7c62a9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b7c62a9 was b7c62a9, checked in by Jiri Zarevucky <zarevucky.jiri@…>, 12 years ago

Make the server oblivious to the link count. It is just another source of potential problems. Also clean up some confusion with file types and node refcount.

  • Property mode set to 100644
File size: 7.8 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file vfs_node.c
35 * @brief Various operations on VFS nodes have their home in this file.
36 */
37
38#include "vfs.h"
39#include <stdlib.h>
40#include <str.h>
41#include <fibril_synch.h>
42#include <adt/hash_table.h>
43#include <adt/hash.h>
44#include <assert.h>
45#include <async.h>
46#include <errno.h>
47
48/** Mutex protecting the VFS node hash table. */
49FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
50
51#define NODES_BUCKETS_LOG 8
52#define NODES_BUCKETS (1 << NODES_BUCKETS_LOG)
53
54/** VFS node hash table containing all active, in-memory VFS nodes. */
55hash_table_t nodes;
56
57#define KEY_FS_HANDLE 0
58#define KEY_DEV_HANDLE 1
59#define KEY_INDEX 2
60
61static size_t nodes_key_hash(void *);
62static size_t nodes_hash(const ht_link_t *);
63static bool nodes_key_equal(void *, const ht_link_t *);
64static vfs_triplet_t node_triplet(vfs_node_t *node);
65
66/** VFS node hash table operations. */
67hash_table_ops_t nodes_ops = {
68 .hash = nodes_hash,
69 .key_hash = nodes_key_hash,
70 .key_equal = nodes_key_equal,
71 .equal = NULL,
72 .remove_callback = NULL,
73};
74
75/** Initialize the VFS node hash table.
76 *
77 * @return Return true on success, false on failure.
78 */
79bool vfs_nodes_init(void)
80{
81 return hash_table_create(&nodes, 0, 0, &nodes_ops);
82}
83
84static inline void _vfs_node_addref(vfs_node_t *node)
85{
86 node->refcnt++;
87}
88
89/** Increment reference count of a VFS node.
90 *
91 * @param node VFS node that will have its refcnt incremented.
92 */
93void vfs_node_addref(vfs_node_t *node)
94{
95 fibril_mutex_lock(&nodes_mutex);
96 _vfs_node_addref(node);
97 fibril_mutex_unlock(&nodes_mutex);
98}
99
100/** Decrement reference count of a VFS node.
101 *
102 * This function handles the case when the reference count drops to zero.
103 *
104 * @param node VFS node that will have its refcnt decremented.
105 */
106void vfs_node_delref(vfs_node_t *node)
107{
108 bool free_node = false;
109
110 fibril_mutex_lock(&nodes_mutex);
111
112 node->refcnt--;
113 if (node->refcnt == 0) {
114
115 /*
116 * We are dropping the last reference to this node.
117 * Remove it from the VFS node hash table.
118 */
119
120 hash_table_remove_item(&nodes, &node->nh_link);
121 free_node = true;
122 }
123
124 fibril_mutex_unlock(&nodes_mutex);
125
126 if (free_node) {
127 /*
128 * DESTROY will free up the file's resources if there are no more hard links.
129 */
130
131 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
132 async_msg_2(exch, VFS_OUT_DESTROY,
133 (sysarg_t) node->service_id, (sysarg_t)node->index);
134 vfs_exchange_release(exch);
135
136 free(node);
137 }
138}
139
140/** Forget node.
141 *
142 * This function will remove the node from the node hash table and deallocate
143 * its memory, regardless of the node's reference count.
144 *
145 * @param node Node to be forgotten.
146 */
147void vfs_node_forget(vfs_node_t *node)
148{
149 fibril_mutex_lock(&nodes_mutex);
150 hash_table_remove_item(&nodes, &node->nh_link);
151 fibril_mutex_unlock(&nodes_mutex);
152 free(node);
153}
154
155/** Find VFS node.
156 *
157 * This function will try to lookup the given triplet in the VFS node hash
158 * table. In case the triplet is not found there, a new VFS node is created.
159 * In any case, the VFS node will have its reference count incremented. Every
160 * node returned by this call should be eventually put back by calling
161 * vfs_node_put() on it.
162 *
163 * @param result Populated lookup result structure.
164 *
165 * @return VFS node corresponding to the given triplet.
166 */
167vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
168{
169 vfs_node_t *node;
170
171 fibril_mutex_lock(&nodes_mutex);
172 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
173 if (!tmp) {
174 node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
175 if (!node) {
176 fibril_mutex_unlock(&nodes_mutex);
177 return NULL;
178 }
179 memset(node, 0, sizeof(vfs_node_t));
180 node->fs_handle = result->triplet.fs_handle;
181 node->service_id = result->triplet.service_id;
182 node->index = result->triplet.index;
183 node->size = result->size;
184 node->type = result->type;
185 fibril_rwlock_initialize(&node->contents_rwlock);
186 hash_table_insert(&nodes, &node->nh_link);
187 } else {
188 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
189 }
190
191 _vfs_node_addref(node);
192 fibril_mutex_unlock(&nodes_mutex);
193
194 return node;
195}
196
197/** Return VFS node when no longer needed by the caller.
198 *
199 * This function will remove the reference on the VFS node created by
200 * vfs_node_get(). This function can only be called as a closing bracket to the
201 * preceding vfs_node_get() call.
202 *
203 * @param node VFS node being released.
204 */
205void vfs_node_put(vfs_node_t *node)
206{
207 vfs_node_delref(node);
208}
209
210struct refcnt_data {
211 /** Sum of all reference counts for this file system instance. */
212 unsigned refcnt;
213 fs_handle_t fs_handle;
214 service_id_t service_id;
215};
216
217static bool refcnt_visitor(ht_link_t *item, void *arg)
218{
219 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
220 struct refcnt_data *rd = (void *) arg;
221
222 if ((node->fs_handle == rd->fs_handle) &&
223 (node->service_id == rd->service_id))
224 rd->refcnt += node->refcnt;
225
226 return true;
227}
228
229unsigned
230vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id)
231{
232 struct refcnt_data rd = {
233 .refcnt = 0,
234 .fs_handle = fs_handle,
235 .service_id = service_id
236 };
237
238 fibril_mutex_lock(&nodes_mutex);
239 hash_table_apply(&nodes, refcnt_visitor, &rd);
240 fibril_mutex_unlock(&nodes_mutex);
241
242 return rd.refcnt;
243}
244
245
246/** Perform a remote node open operation.
247 *
248 * @return EOK on success or an error code from errno.h.
249 *
250 */
251int vfs_open_node_remote(vfs_node_t *node)
252{
253 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
254
255 ipc_call_t answer;
256 aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
257 (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
258
259 vfs_exchange_release(exch);
260
261 sysarg_t rc;
262 async_wait_for(req, &rc);
263
264 return rc;
265}
266
267
268static size_t nodes_key_hash(void *key)
269{
270 vfs_triplet_t *tri = key;
271 size_t hash = hash_combine(tri->fs_handle, tri->index);
272 return hash_combine(hash, tri->service_id);
273}
274
275static size_t nodes_hash(const ht_link_t *item)
276{
277 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
278 vfs_triplet_t tri = node_triplet(node);
279 return nodes_key_hash(&tri);
280}
281
282static bool nodes_key_equal(void *key, const ht_link_t *item)
283{
284 vfs_triplet_t *tri = key;
285 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
286 return node->fs_handle == tri->fs_handle
287 && node->service_id == tri->service_id
288 && node->index == tri->index;
289}
290
291static inline vfs_triplet_t node_triplet(vfs_node_t *node)
292{
293 vfs_triplet_t tri = {
294 .fs_handle = node->fs_handle,
295 .service_id = node->service_id,
296 .index = node->index
297 };
298
299 return tri;
300}
301
302/**
303 * @}
304 */
Note: See TracBrowser for help on using the repository browser.