1 /* $NetBSD: kern_fileassoc.c,v 1.33 2009/12/25 20:05:43 elad Exp $ */
4 * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: kern_fileassoc.c,v 1.33 2009/12/25 20:05:43 elad Exp $");
33 #include "opt_fileassoc.h"
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/queue.h>
38 #include <sys/vnode.h>
39 #include <sys/errno.h>
40 #include <sys/fileassoc.h>
41 #include <sys/specificdata.h>
46 #define FILEASSOC_INITIAL_TABLESIZE 128
48 static specificdata_domain_t fileassoc_domain
;
49 static specificdata_key_t fileassoc_mountspecific_key
;
50 static ONCE_DECL(control
);
54 * Includes the assoc name for identification and private clear callback.
57 LIST_ENTRY(fileassoc
) assoc_list
;
58 const char *assoc_name
; /* Name. */
59 fileassoc_cleanup_cb_t assoc_cleanup_cb
; /* Clear callback. */
60 specificdata_key_t assoc_key
;
63 static LIST_HEAD(, fileassoc
) fileassoc_list
;
65 /* An entry in the per-mount hash table. */
66 struct fileassoc_file
{
67 fhandle_t
*faf_handle
; /* File handle */
68 specificdata_reference faf_data
; /* Assoc data. */
69 u_int faf_nassocs
; /* # of assocs. */
70 LIST_ENTRY(fileassoc_file
) faf_list
; /* List pointer. */
73 LIST_HEAD(fileassoc_hash_entry
, fileassoc_file
);
75 struct fileassoc_table
{
76 struct fileassoc_hash_entry
*tbl_hash
;
77 u_long tbl_mask
; /* Hash table mask. */
78 size_t tbl_nslots
; /* Number of slots. */
79 size_t tbl_nused
; /* # of used slots. */
80 specificdata_reference tbl_data
;
84 * Hashing function: Takes a number modulus the mask to give back an
85 * index into the hash table.
87 #define FILEASSOC_HASH(tbl, handle) \
88 (hash32_buf((handle), FHANDLE_SIZE(handle), HASH32_BUF_INIT) \
92 file_getdata(struct fileassoc_file
*faf
, const struct fileassoc
*assoc
)
95 return specificdata_getspecific(fileassoc_domain
, &faf
->faf_data
,
100 file_setdata(struct fileassoc_file
*faf
, const struct fileassoc
*assoc
,
104 specificdata_setspecific(fileassoc_domain
, &faf
->faf_data
,
105 assoc
->assoc_key
, data
);
109 file_cleanup(struct fileassoc_file
*faf
, const struct fileassoc
*assoc
)
111 fileassoc_cleanup_cb_t cb
;
114 cb
= assoc
->assoc_cleanup_cb
;
118 data
= file_getdata(faf
, assoc
);
123 file_free(struct fileassoc_file
*faf
)
125 struct fileassoc
*assoc
;
127 LIST_REMOVE(faf
, faf_list
);
129 LIST_FOREACH(assoc
, &fileassoc_list
, assoc_list
) {
130 file_cleanup(faf
, assoc
);
132 vfs_composefh_free(faf
->faf_handle
);
133 specificdata_fini(fileassoc_domain
, &faf
->faf_data
);
134 kmem_free(faf
, sizeof(*faf
));
140 struct fileassoc_table
*tbl
= v
;
143 /* Remove all entries from the table and lists */
144 for (i
= 0; i
< tbl
->tbl_nslots
; i
++) {
145 struct fileassoc_file
*faf
;
147 while ((faf
= LIST_FIRST(&tbl
->tbl_hash
[i
])) != NULL
) {
152 /* Remove hash table and sysctl node */
153 hashdone(tbl
->tbl_hash
, HASH_LIST
, tbl
->tbl_mask
);
154 specificdata_fini(fileassoc_domain
, &tbl
->tbl_data
);
155 kmem_free(tbl
, sizeof(*tbl
));
159 * Initialize the fileassoc subsystem.
166 error
= mount_specific_key_create(&fileassoc_mountspecific_key
,
171 fileassoc_domain
= specificdata_domain_create();
177 * Register a new assoc.
180 fileassoc_register(const char *name
, fileassoc_cleanup_cb_t cleanup_cb
,
184 specificdata_key_t key
;
185 struct fileassoc
*assoc
;
187 error
= RUN_ONCE(&control
, fileassoc_init
);
191 error
= specificdata_key_create(fileassoc_domain
, &key
, NULL
);
195 assoc
= kmem_alloc(sizeof(*assoc
), KM_SLEEP
);
196 assoc
->assoc_name
= name
;
197 assoc
->assoc_cleanup_cb
= cleanup_cb
;
198 assoc
->assoc_key
= key
;
200 LIST_INSERT_HEAD(&fileassoc_list
, assoc
, assoc_list
);
208 * Deregister an assoc.
211 fileassoc_deregister(fileassoc_t assoc
)
214 LIST_REMOVE(assoc
, assoc_list
);
215 specificdata_key_delete(fileassoc_domain
, assoc
->assoc_key
);
216 kmem_free(assoc
, sizeof(*assoc
));
222 * Get the hash table for the specified device.
224 static struct fileassoc_table
*
225 fileassoc_table_lookup(struct mount
*mp
)
229 error
= RUN_ONCE(&control
, fileassoc_init
);
233 return mount_getspecific(mp
, fileassoc_mountspecific_key
);
237 * Perform a lookup on a hash table. If hint is non-zero then use the value
238 * of the hint as the identifier instead of performing a lookup for the
241 static struct fileassoc_file
*
242 fileassoc_file_lookup(struct vnode
*vp
, fhandle_t
*hint
)
244 struct fileassoc_table
*tbl
;
245 struct fileassoc_hash_entry
*hash_entry
;
246 struct fileassoc_file
*faf
;
251 tbl
= fileassoc_table_lookup(vp
->v_mount
);
257 error
= vfs_composefh_alloc(vp
, &th
);
264 indx
= FILEASSOC_HASH(tbl
, th
);
265 hash_entry
= &(tbl
->tbl_hash
[indx
]);
267 LIST_FOREACH(faf
, hash_entry
, faf_list
) {
268 if (((FHANDLE_FILEID(faf
->faf_handle
)->fid_len
==
269 FHANDLE_FILEID(th
)->fid_len
)) &&
270 (memcmp(FHANDLE_FILEID(faf
->faf_handle
), FHANDLE_FILEID(th
),
271 (FHANDLE_FILEID(th
))->fid_len
) == 0)) {
277 vfs_composefh_free(th
);
283 * Return assoc data associated with a vnode.
286 fileassoc_lookup(struct vnode
*vp
, fileassoc_t assoc
)
288 struct fileassoc_file
*faf
;
290 faf
= fileassoc_file_lookup(vp
, NULL
);
294 return file_getdata(faf
, assoc
);
297 static struct fileassoc_table
*
298 fileassoc_table_resize(struct fileassoc_table
*tbl
)
300 struct fileassoc_table
*newtbl
;
304 * Allocate a new table. Like the condition in fileassoc_file_add(),
305 * this is also temporary -- just double the number of slots.
307 newtbl
= kmem_zalloc(sizeof(*newtbl
), KM_SLEEP
);
308 newtbl
->tbl_nslots
= (tbl
->tbl_nslots
* 2);
309 if (newtbl
->tbl_nslots
< tbl
->tbl_nslots
)
310 newtbl
->tbl_nslots
= tbl
->tbl_nslots
;
311 newtbl
->tbl_hash
= hashinit(newtbl
->tbl_nslots
, HASH_LIST
,
312 true, &newtbl
->tbl_mask
);
313 newtbl
->tbl_nused
= 0;
314 specificdata_init(fileassoc_domain
, &newtbl
->tbl_data
);
316 /* XXX we need to make sure nothing uses fileassoc here! */
318 for (i
= 0; i
< tbl
->tbl_nslots
; i
++) {
319 struct fileassoc_file
*faf
;
321 while ((faf
= LIST_FIRST(&tbl
->tbl_hash
[i
])) != NULL
) {
322 struct fileassoc_hash_entry
*hash_entry
;
325 LIST_REMOVE(faf
, faf_list
);
327 indx
= FILEASSOC_HASH(newtbl
, faf
->faf_handle
);
328 hash_entry
= &(newtbl
->tbl_hash
[indx
]);
330 LIST_INSERT_HEAD(hash_entry
, faf
, faf_list
);
336 if (tbl
->tbl_nused
!= newtbl
->tbl_nused
)
337 panic("fileassoc_table_resize: inconsistency detected! "
338 "needed %zu entries, got %zu", tbl
->tbl_nused
,
341 hashdone(tbl
->tbl_hash
, HASH_LIST
, tbl
->tbl_mask
);
342 specificdata_fini(fileassoc_domain
, &tbl
->tbl_data
);
343 kmem_free(tbl
, sizeof(*tbl
));
349 * Create a new fileassoc table.
351 static struct fileassoc_table
*
352 fileassoc_table_add(struct mount
*mp
)
354 struct fileassoc_table
*tbl
;
356 /* Check for existing table for device. */
357 tbl
= fileassoc_table_lookup(mp
);
361 /* Allocate and initialize a table. */
362 tbl
= kmem_zalloc(sizeof(*tbl
), KM_SLEEP
);
363 tbl
->tbl_nslots
= FILEASSOC_INITIAL_TABLESIZE
;
364 tbl
->tbl_hash
= hashinit(tbl
->tbl_nslots
, HASH_LIST
, true,
367 specificdata_init(fileassoc_domain
, &tbl
->tbl_data
);
369 mount_setspecific(mp
, fileassoc_mountspecific_key
, tbl
);
378 fileassoc_table_delete(struct mount
*mp
)
380 struct fileassoc_table
*tbl
;
382 tbl
= fileassoc_table_lookup(mp
);
386 mount_setspecific(mp
, fileassoc_mountspecific_key
, NULL
);
393 * Run a callback for each assoc in a table.
396 fileassoc_table_run(struct mount
*mp
, fileassoc_t assoc
, fileassoc_cb_t cb
,
399 struct fileassoc_table
*tbl
;
402 tbl
= fileassoc_table_lookup(mp
);
406 for (i
= 0; i
< tbl
->tbl_nslots
; i
++) {
407 struct fileassoc_file
*faf
;
409 LIST_FOREACH(faf
, &tbl
->tbl_hash
[i
], faf_list
) {
412 data
= file_getdata(faf
, assoc
);
422 * Clear a table for a given assoc.
425 fileassoc_table_clear(struct mount
*mp
, fileassoc_t assoc
)
427 struct fileassoc_table
*tbl
;
430 tbl
= fileassoc_table_lookup(mp
);
434 for (i
= 0; i
< tbl
->tbl_nslots
; i
++) {
435 struct fileassoc_file
*faf
;
437 LIST_FOREACH(faf
, &tbl
->tbl_hash
[i
], faf_list
) {
438 file_cleanup(faf
, assoc
);
439 file_setdata(faf
, assoc
, NULL
);
447 * Add a file entry to a table.
449 static struct fileassoc_file
*
450 fileassoc_file_add(struct vnode
*vp
, fhandle_t
*hint
)
452 struct fileassoc_table
*tbl
;
453 struct fileassoc_hash_entry
*hash_entry
;
454 struct fileassoc_file
*faf
;
460 error
= vfs_composefh_alloc(vp
, &th
);
466 faf
= fileassoc_file_lookup(vp
, th
);
469 vfs_composefh_free(th
);
474 tbl
= fileassoc_table_lookup(vp
->v_mount
);
476 tbl
= fileassoc_table_add(vp
->v_mount
);
479 indx
= FILEASSOC_HASH(tbl
, th
);
480 hash_entry
= &(tbl
->tbl_hash
[indx
]);
482 faf
= kmem_zalloc(sizeof(*faf
), KM_SLEEP
);
483 faf
->faf_handle
= th
;
484 specificdata_init(fileassoc_domain
, &faf
->faf_data
);
485 LIST_INSERT_HEAD(hash_entry
, faf
, faf_list
);
488 * This decides when we need to resize the table. For now,
489 * resize it whenever we "filled" up the number of slots it
490 * has. That's not really true unless of course we had zero
491 * collisions. Think positive! :)
493 if (++(tbl
->tbl_nused
) == tbl
->tbl_nslots
) {
494 struct fileassoc_table
*newtbl
;
496 newtbl
= fileassoc_table_resize(tbl
);
497 mount_setspecific(vp
->v_mount
, fileassoc_mountspecific_key
,
505 * Delete a file entry from a table.
508 fileassoc_file_delete(struct vnode
*vp
)
510 struct fileassoc_table
*tbl
;
511 struct fileassoc_file
*faf
;
513 KERNEL_LOCK(1, NULL
);
515 faf
= fileassoc_file_lookup(vp
, NULL
);
517 KERNEL_UNLOCK_ONE(NULL
);
523 tbl
= fileassoc_table_lookup(vp
->v_mount
);
524 --(tbl
->tbl_nused
); /* XXX gc? */
526 KERNEL_UNLOCK_ONE(NULL
);
532 * Add an assoc to a vnode.
535 fileassoc_add(struct vnode
*vp
, fileassoc_t assoc
, void *data
)
537 struct fileassoc_file
*faf
;
540 faf
= fileassoc_file_lookup(vp
, NULL
);
542 faf
= fileassoc_file_add(vp
, NULL
);
547 olddata
= file_getdata(faf
, assoc
);
551 file_setdata(faf
, assoc
, data
);
559 * Clear an assoc from a vnode.
562 fileassoc_clear(struct vnode
*vp
, fileassoc_t assoc
)
564 struct fileassoc_file
*faf
;
566 faf
= fileassoc_file_lookup(vp
, NULL
);
570 file_cleanup(faf
, assoc
);
571 file_setdata(faf
, assoc
, NULL
);
573 --(faf
->faf_nassocs
); /* XXX gc? */