source: mainline/uspace/srv/vfs/vfs_node.c@ 08042bd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 08042bd was df908b3, checked in by Jakub Jermar <jakub@…>, 15 years ago

Use async relations introduced in the previous changeset to implement
vfs_grab_phone() and vfs_release_phone().

With this change, VFS will not be pointlessly connecting and disconnecting
phones on each VFS request.

Judging from the output of the top utility, this reduces the share of VFS on
kernel time by about 7% (from 25% down to 18%) and DEVFS kernel time by about 5%
(from 28% to 23%). This also makes CONSOLE the biggest consumer of kernel time,
while it used to be third after DEVFS and VFS.

  • Property mode set to 100644
File size: 7.9 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 <assert.h>
44#include <async.h>
45#include <errno.h>
46
47/** Mutex protecting the VFS node hash table. */
48FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
49
50#define NODES_BUCKETS_LOG 8
51#define NODES_BUCKETS (1 << NODES_BUCKETS_LOG)
52
53/** VFS node hash table containing all active, in-memory VFS nodes. */
54hash_table_t nodes;
55
56#define KEY_FS_HANDLE 0
57#define KEY_DEV_HANDLE 1
58#define KEY_INDEX 2
59
60static hash_index_t nodes_hash(unsigned long []);
61static int nodes_compare(unsigned long [], hash_count_t, link_t *);
62static void nodes_remove_callback(link_t *);
63
64/** VFS node hash table operations. */
65hash_table_operations_t nodes_ops = {
66 .hash = nodes_hash,
67 .compare = nodes_compare,
68 .remove_callback = nodes_remove_callback
69};
70
71/** Initialize the VFS node hash table.
72 *
73 * @return Return true on success, false on failure.
74 */
75bool vfs_nodes_init(void)
76{
77 return hash_table_create(&nodes, NODES_BUCKETS, 3, &nodes_ops);
78}
79
80static inline void _vfs_node_addref(vfs_node_t *node)
81{
82 node->refcnt++;
83}
84
85/** Increment reference count of a VFS node.
86 *
87 * @param node VFS node that will have its refcnt incremented.
88 */
89void vfs_node_addref(vfs_node_t *node)
90{
91 fibril_mutex_lock(&nodes_mutex);
92 _vfs_node_addref(node);
93 fibril_mutex_unlock(&nodes_mutex);
94}
95
96/** Decrement reference count of a VFS node.
97 *
98 * This function handles the case when the reference count drops to zero.
99 *
100 * @param node VFS node that will have its refcnt decremented.
101 */
102void vfs_node_delref(vfs_node_t *node)
103{
104 bool free_vfs_node = false;
105 bool free_fs_node = false;
106
107 fibril_mutex_lock(&nodes_mutex);
108 if (node->refcnt-- == 1) {
109 /*
110 * We are dropping the last reference to this node.
111 * Remove it from the VFS node hash table.
112 */
113 unsigned long key[] = {
114 [KEY_FS_HANDLE] = node->fs_handle,
115 [KEY_DEV_HANDLE] = node->dev_handle,
116 [KEY_INDEX] = node->index
117 };
118 hash_table_remove(&nodes, key, 3);
119 free_vfs_node = true;
120 if (!node->lnkcnt)
121 free_fs_node = true;
122 }
123 fibril_mutex_unlock(&nodes_mutex);
124
125 if (free_fs_node) {
126 /*
127 * The node is not visible in the file system namespace.
128 * Free up its resources.
129 */
130 int phone = vfs_grab_phone(node->fs_handle);
131 ipcarg_t rc;
132 rc = async_req_2_0(phone, VFS_OUT_DESTROY,
133 (ipcarg_t)node->dev_handle, (ipcarg_t)node->index);
134 assert(rc == EOK);
135 vfs_release_phone(node->fs_handle, phone);
136 }
137 if (free_vfs_node)
138 free(node);
139}
140
141/** Forget node.
142 *
143 * This function will remove the node from the node hash table and deallocate
144 * its memory, regardless of the node's reference count.
145 *
146 * @param node Node to be forgotten.
147 */
148void vfs_node_forget(vfs_node_t *node)
149{
150 fibril_mutex_lock(&nodes_mutex);
151 unsigned long key[] = {
152 [KEY_FS_HANDLE] = node->fs_handle,
153 [KEY_DEV_HANDLE] = node->dev_handle,
154 [KEY_INDEX] = node->index
155 };
156 hash_table_remove(&nodes, key, 3);
157 fibril_mutex_unlock(&nodes_mutex);
158 free(node);
159}
160
161/** Find VFS node.
162 *
163 * This function will try to lookup the given triplet in the VFS node hash
164 * table. In case the triplet is not found there, a new VFS node is created.
165 * In any case, the VFS node will have its reference count incremented. Every
166 * node returned by this call should be eventually put back by calling
167 * vfs_node_put() on it.
168 *
169 * @param result Populated lookup result structure.
170 *
171 * @return VFS node corresponding to the given triplet.
172 */
173vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
174{
175 unsigned long key[] = {
176 [KEY_FS_HANDLE] = result->triplet.fs_handle,
177 [KEY_DEV_HANDLE] = result->triplet.dev_handle,
178 [KEY_INDEX] = result->triplet.index
179 };
180 link_t *tmp;
181 vfs_node_t *node;
182
183 fibril_mutex_lock(&nodes_mutex);
184 tmp = hash_table_find(&nodes, key);
185 if (!tmp) {
186 node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
187 if (!node) {
188 fibril_mutex_unlock(&nodes_mutex);
189 return NULL;
190 }
191 memset(node, 0, sizeof(vfs_node_t));
192 node->fs_handle = result->triplet.fs_handle;
193 node->dev_handle = result->triplet.dev_handle;
194 node->index = result->triplet.index;
195 node->size = result->size;
196 node->lnkcnt = result->lnkcnt;
197 node->type = result->type;
198 link_initialize(&node->nh_link);
199 fibril_rwlock_initialize(&node->contents_rwlock);
200 hash_table_insert(&nodes, key, &node->nh_link);
201 } else {
202 node = hash_table_get_instance(tmp, vfs_node_t, nh_link);
203 if (node->type == VFS_NODE_UNKNOWN &&
204 result->type != VFS_NODE_UNKNOWN) {
205 /* Upgrade the node type. */
206 node->type = result->type;
207 }
208 }
209
210 assert(node->size == result->size || node->type != VFS_NODE_FILE);
211 assert(node->lnkcnt == result->lnkcnt);
212 assert(node->type == result->type || result->type == VFS_NODE_UNKNOWN);
213
214 _vfs_node_addref(node);
215 fibril_mutex_unlock(&nodes_mutex);
216
217 return node;
218}
219
220/** Return VFS node when no longer needed by the caller.
221 *
222 * This function will remove the reference on the VFS node created by
223 * vfs_node_get(). This function can only be called as a closing bracket to the
224 * preceding vfs_node_get() call.
225 *
226 * @param node VFS node being released.
227 */
228void vfs_node_put(vfs_node_t *node)
229{
230 vfs_node_delref(node);
231}
232
233hash_index_t nodes_hash(unsigned long key[])
234{
235 hash_index_t a = key[KEY_FS_HANDLE] << (NODES_BUCKETS_LOG / 4);
236 hash_index_t b = (a | key[KEY_DEV_HANDLE]) << (NODES_BUCKETS_LOG / 2);
237
238 return (b | key[KEY_INDEX]) & (NODES_BUCKETS - 1);
239}
240
241int nodes_compare(unsigned long key[], hash_count_t keys, link_t *item)
242{
243 vfs_node_t *node = hash_table_get_instance(item, vfs_node_t, nh_link);
244 return (node->fs_handle == (fs_handle_t) key[KEY_FS_HANDLE]) &&
245 (node->dev_handle == key[KEY_DEV_HANDLE]) &&
246 (node->index == key[KEY_INDEX]);
247}
248
249void nodes_remove_callback(link_t *item)
250{
251}
252
253struct refcnt_data {
254 /** Sum of all reference counts for this file system instance. */
255 unsigned refcnt;
256 fs_handle_t fs_handle;
257 dev_handle_t dev_handle;
258};
259
260static void refcnt_visitor(link_t *item, void *arg)
261{
262 vfs_node_t *node = hash_table_get_instance(item, vfs_node_t, nh_link);
263 struct refcnt_data *rd = (void *) arg;
264
265 if ((node->fs_handle == rd->fs_handle) &&
266 (node->dev_handle == rd->dev_handle))
267 rd->refcnt += node->refcnt;
268}
269
270unsigned
271vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, dev_handle_t dev_handle)
272{
273 struct refcnt_data rd = {
274 .refcnt = 0,
275 .fs_handle = fs_handle,
276 .dev_handle = dev_handle
277 };
278
279 fibril_mutex_lock(&nodes_mutex);
280 hash_table_apply(&nodes, refcnt_visitor, &rd);
281 fibril_mutex_unlock(&nodes_mutex);
282
283 return rd.refcnt;
284}
285
286/**
287 * @}
288 */
Note: See TracBrowser for help on using the repository browser.