mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	cachefiles: Implement volume support
Implement support for creating the directory layout for a volume on disk and setting up and withdrawing volume caching. Each volume has a directory named for the volume key under the root of the cache (prefixed with an 'I' to indicate to cachefilesd that it's an index) and then creates a bunch of hash bucket subdirectories under that (named as '@' plus a hex number) in which cookie files will be created. Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819635314.215744.13081522301564537723.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906936397.143852.17788457778396467161.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967143860.1823006.7185205806080225038.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021545212.640689.5064821392307582927.stgit@warthog.procyon.org.uk/ # v4
This commit is contained in:
		
							parent
							
								
									d1065b0a6f
								
							
						
					
					
						commit
						fe2140e2f5
					
				
					 6 changed files with 171 additions and 2 deletions
				
			
		|  | @ -9,7 +9,8 @@ cachefiles-y := \ | |||
| 	interface.o \
 | ||||
| 	main.o \
 | ||||
| 	namei.o \
 | ||||
| 	security.o | ||||
| 	security.o \
 | ||||
| 	volume.o | ||||
| 
 | ||||
| cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o | ||||
| 
 | ||||
|  |  | |||
|  | @ -262,6 +262,32 @@ int cachefiles_has_space(struct cachefiles_cache *cache, | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Withdraw volumes. | ||||
|  */ | ||||
| static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache) | ||||
| { | ||||
| 	_enter(""); | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		struct cachefiles_volume *volume = NULL; | ||||
| 
 | ||||
| 		spin_lock(&cache->object_list_lock); | ||||
| 		if (!list_empty(&cache->volumes)) { | ||||
| 			volume = list_first_entry(&cache->volumes, | ||||
| 						  struct cachefiles_volume, cache_link); | ||||
| 			list_del_init(&volume->cache_link); | ||||
| 		} | ||||
| 		spin_unlock(&cache->object_list_lock); | ||||
| 		if (!volume) | ||||
| 			break; | ||||
| 
 | ||||
| 		cachefiles_withdraw_volume(volume); | ||||
| 	} | ||||
| 
 | ||||
| 	_leave(""); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Sync a cache to backing disk. | ||||
|  */ | ||||
|  | @ -303,7 +329,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache) | |||
| 	// PLACEHOLDER: Withdraw objects
 | ||||
| 	fscache_wait_for_objects(fscache); | ||||
| 
 | ||||
| 	// PLACEHOLDER: Withdraw volume
 | ||||
| 	cachefiles_withdraw_volumes(cache); | ||||
| 	cachefiles_sync_cache(cache); | ||||
| 	cache->cache = NULL; | ||||
| 	fscache_relinquish_cache(fscache); | ||||
|  |  | |||
|  | @ -105,6 +105,8 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file) | |||
| 
 | ||||
| 	mutex_init(&cache->daemon_mutex); | ||||
| 	init_waitqueue_head(&cache->daemon_pollwq); | ||||
| 	INIT_LIST_HEAD(&cache->volumes); | ||||
| 	spin_lock_init(&cache->object_list_lock); | ||||
| 
 | ||||
| 	/* set default caching limits
 | ||||
| 	 * - limit at 1% free space and/or free files | ||||
|  |  | |||
|  | @ -15,4 +15,6 @@ | |||
| 
 | ||||
| const struct fscache_cache_ops cachefiles_cache_ops = { | ||||
| 	.name			= "cachefiles", | ||||
| 	.acquire_volume		= cachefiles_acquire_volume, | ||||
| 	.free_volume		= cachefiles_free_volume, | ||||
| }; | ||||
|  |  | |||
|  | @ -19,6 +19,17 @@ | |||
| struct cachefiles_cache; | ||||
| struct cachefiles_object; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cached volume representation. | ||||
|  */ | ||||
| struct cachefiles_volume { | ||||
| 	struct cachefiles_cache		*cache; | ||||
| 	struct list_head		cache_link;	/* Link in cache->volumes */ | ||||
| 	struct fscache_volume		*vcookie;	/* The netfs's representation */ | ||||
| 	struct dentry			*dentry;	/* The volume dentry */ | ||||
| 	struct dentry			*fanout[256];	/* Fanout subdirs */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Data file records. | ||||
|  */ | ||||
|  | @ -35,6 +46,8 @@ struct cachefiles_cache { | |||
| 	struct dentry			*store;		/* Directory into which live objects go */ | ||||
| 	struct dentry			*graveyard;	/* directory into which dead objects go */ | ||||
| 	struct file			*cachefilesd;	/* manager daemon handle */ | ||||
| 	struct list_head		volumes;	/* List of volume objects */ | ||||
| 	spinlock_t			object_list_lock; /* Lock for volumes and object_list */ | ||||
| 	const struct cred		*cache_cred;	/* security override for accessing cache */ | ||||
| 	struct mutex			daemon_mutex;	/* command serialisation mutex */ | ||||
| 	wait_queue_head_t		daemon_pollwq;	/* poll waitqueue for daemon */ | ||||
|  | @ -163,6 +176,13 @@ static inline void cachefiles_end_secure(struct cachefiles_cache *cache, | |||
| 	revert_creds(saved_cred); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * volume.c | ||||
|  */ | ||||
| void cachefiles_acquire_volume(struct fscache_volume *volume); | ||||
| void cachefiles_free_volume(struct fscache_volume *volume); | ||||
| void cachefiles_withdraw_volume(struct cachefiles_volume *volume); | ||||
| 
 | ||||
| /*
 | ||||
|  * Error handling | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										118
									
								
								fs/cachefiles/volume.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								fs/cachefiles/volume.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,118 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||
| /* Volume handling.
 | ||||
|  * | ||||
|  * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. | ||||
|  * Written by David Howells (dhowells@redhat.com) | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/fs.h> | ||||
| #include <linux/slab.h> | ||||
| #include "internal.h" | ||||
| #include <trace/events/fscache.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Allocate and set up a volume representation.  We make sure all the fanout | ||||
|  * directories are created and pinned. | ||||
|  */ | ||||
| void cachefiles_acquire_volume(struct fscache_volume *vcookie) | ||||
| { | ||||
| 	struct cachefiles_volume *volume; | ||||
| 	struct cachefiles_cache *cache = vcookie->cache->cache_priv; | ||||
| 	const struct cred *saved_cred; | ||||
| 	struct dentry *vdentry, *fan; | ||||
| 	size_t len; | ||||
| 	char *name; | ||||
| 	int n_accesses, i; | ||||
| 
 | ||||
| 	_enter(""); | ||||
| 
 | ||||
| 	volume = kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL); | ||||
| 	if (!volume) | ||||
| 		return; | ||||
| 	volume->vcookie = vcookie; | ||||
| 	volume->cache = cache; | ||||
| 	INIT_LIST_HEAD(&volume->cache_link); | ||||
| 
 | ||||
| 	cachefiles_begin_secure(cache, &saved_cred); | ||||
| 
 | ||||
| 	len = vcookie->key[0]; | ||||
| 	name = kmalloc(len + 3, GFP_NOFS); | ||||
| 	if (!name) | ||||
| 		goto error_vol; | ||||
| 	name[0] = 'I'; | ||||
| 	memcpy(name + 1, vcookie->key + 1, len); | ||||
| 	name[len + 1] = 0; | ||||
| 
 | ||||
| 	vdentry = cachefiles_get_directory(cache, cache->store, name, NULL); | ||||
| 	if (IS_ERR(vdentry)) | ||||
| 		goto error_name; | ||||
| 	volume->dentry = vdentry; | ||||
| 
 | ||||
| 	for (i = 0; i < 256; i++) { | ||||
| 		sprintf(name, "@%02x", i); | ||||
| 		fan = cachefiles_get_directory(cache, vdentry, name, NULL); | ||||
| 		if (IS_ERR(fan)) | ||||
| 			goto error_fan; | ||||
| 		volume->fanout[i] = fan; | ||||
| 	} | ||||
| 
 | ||||
| 	cachefiles_end_secure(cache, saved_cred); | ||||
| 
 | ||||
| 	vcookie->cache_priv = volume; | ||||
| 	n_accesses = atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups on dec-to-0 */ | ||||
| 	trace_fscache_access_volume(vcookie->debug_id, 0, | ||||
| 				    refcount_read(&vcookie->ref), | ||||
| 				    n_accesses, fscache_access_cache_pin); | ||||
| 
 | ||||
| 	spin_lock(&cache->object_list_lock); | ||||
| 	list_add(&volume->cache_link, &volume->cache->volumes); | ||||
| 	spin_unlock(&cache->object_list_lock); | ||||
| 
 | ||||
| 	kfree(name); | ||||
| 	return; | ||||
| 
 | ||||
| error_fan: | ||||
| 	for (i = 0; i < 256; i++) | ||||
| 		cachefiles_put_directory(volume->fanout[i]); | ||||
| 	cachefiles_put_directory(volume->dentry); | ||||
| error_name: | ||||
| 	kfree(name); | ||||
| error_vol: | ||||
| 	kfree(volume); | ||||
| 	cachefiles_end_secure(cache, saved_cred); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Release a volume representation. | ||||
|  */ | ||||
| static void __cachefiles_free_volume(struct cachefiles_volume *volume) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	_enter(""); | ||||
| 
 | ||||
| 	volume->vcookie->cache_priv = NULL; | ||||
| 
 | ||||
| 	for (i = 0; i < 256; i++) | ||||
| 		cachefiles_put_directory(volume->fanout[i]); | ||||
| 	cachefiles_put_directory(volume->dentry); | ||||
| 	kfree(volume); | ||||
| } | ||||
| 
 | ||||
| void cachefiles_free_volume(struct fscache_volume *vcookie) | ||||
| { | ||||
| 	struct cachefiles_volume *volume = vcookie->cache_priv; | ||||
| 
 | ||||
| 	if (volume) { | ||||
| 		spin_lock(&volume->cache->object_list_lock); | ||||
| 		list_del_init(&volume->cache_link); | ||||
| 		spin_unlock(&volume->cache->object_list_lock); | ||||
| 		__cachefiles_free_volume(volume); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void cachefiles_withdraw_volume(struct cachefiles_volume *volume) | ||||
| { | ||||
| 	fscache_withdraw_volume(volume->vcookie); | ||||
| 	__cachefiles_free_volume(volume); | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 David Howells
						David Howells