Downgrade the requirements passed to the allocator in the gfp flags parameter. FS-Cache/CacheFiles can handle OOM conditions simply by aborting the attempt to store an object or a page in the cache. Signed-off-by: David Howells <dhowells@redhat.com>
		
			
				
	
	
		
			292 lines
		
	
	
	
		
			6.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
	
		
			6.4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/* CacheFiles extended attribute management
 | 
						|
 *
 | 
						|
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 | 
						|
 * Written by David Howells (dhowells@redhat.com)
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public Licence
 | 
						|
 * as published by the Free Software Foundation; either version
 | 
						|
 * 2 of the Licence, or (at your option) any later version.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
#include <linux/file.h>
 | 
						|
#include <linux/fs.h>
 | 
						|
#include <linux/fsnotify.h>
 | 
						|
#include <linux/quotaops.h>
 | 
						|
#include <linux/xattr.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include "internal.h"
 | 
						|
 | 
						|
static const char cachefiles_xattr_cache[] =
 | 
						|
	XATTR_USER_PREFIX "CacheFiles.cache";
 | 
						|
 | 
						|
/*
 | 
						|
 * check the type label on an object
 | 
						|
 * - done using xattrs
 | 
						|
 */
 | 
						|
int cachefiles_check_object_type(struct cachefiles_object *object)
 | 
						|
{
 | 
						|
	struct dentry *dentry = object->dentry;
 | 
						|
	char type[3], xtype[3];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ASSERT(dentry);
 | 
						|
	ASSERT(dentry->d_inode);
 | 
						|
 | 
						|
	if (!object->fscache.cookie)
 | 
						|
		strcpy(type, "C3");
 | 
						|
	else
 | 
						|
		snprintf(type, 3, "%02x", object->fscache.cookie->def->type);
 | 
						|
 | 
						|
	_enter("%p{%s}", object, type);
 | 
						|
 | 
						|
	/* attempt to install a type label directly */
 | 
						|
	ret = vfs_setxattr(dentry, cachefiles_xattr_cache, type, 2,
 | 
						|
			   XATTR_CREATE);
 | 
						|
	if (ret == 0) {
 | 
						|
		_debug("SET"); /* we succeeded */
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret != -EEXIST) {
 | 
						|
		kerror("Can't set xattr on %*.*s [%lu] (err %d)",
 | 
						|
		       dentry->d_name.len, dentry->d_name.len,
 | 
						|
		       dentry->d_name.name, dentry->d_inode->i_ino,
 | 
						|
		       -ret);
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	/* read the current type label */
 | 
						|
	ret = vfs_getxattr(dentry, cachefiles_xattr_cache, xtype, 3);
 | 
						|
	if (ret < 0) {
 | 
						|
		if (ret == -ERANGE)
 | 
						|
			goto bad_type_length;
 | 
						|
 | 
						|
		kerror("Can't read xattr on %*.*s [%lu] (err %d)",
 | 
						|
		       dentry->d_name.len, dentry->d_name.len,
 | 
						|
		       dentry->d_name.name, dentry->d_inode->i_ino,
 | 
						|
		       -ret);
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	/* check the type is what we're expecting */
 | 
						|
	if (ret != 2)
 | 
						|
		goto bad_type_length;
 | 
						|
 | 
						|
	if (xtype[0] != type[0] || xtype[1] != type[1])
 | 
						|
		goto bad_type;
 | 
						|
 | 
						|
	ret = 0;
 | 
						|
 | 
						|
error:
 | 
						|
	_leave(" = %d", ret);
 | 
						|
	return ret;
 | 
						|
 | 
						|
bad_type_length:
 | 
						|
	kerror("Cache object %lu type xattr length incorrect",
 | 
						|
	       dentry->d_inode->i_ino);
 | 
						|
	ret = -EIO;
 | 
						|
	goto error;
 | 
						|
 | 
						|
bad_type:
 | 
						|
	xtype[2] = 0;
 | 
						|
	kerror("Cache object %*.*s [%lu] type %s not %s",
 | 
						|
	       dentry->d_name.len, dentry->d_name.len,
 | 
						|
	       dentry->d_name.name, dentry->d_inode->i_ino,
 | 
						|
	       xtype, type);
 | 
						|
	ret = -EIO;
 | 
						|
	goto error;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * set the state xattr on a cache file
 | 
						|
 */
 | 
						|
int cachefiles_set_object_xattr(struct cachefiles_object *object,
 | 
						|
				struct cachefiles_xattr *auxdata)
 | 
						|
{
 | 
						|
	struct dentry *dentry = object->dentry;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ASSERT(object->fscache.cookie);
 | 
						|
	ASSERT(dentry);
 | 
						|
 | 
						|
	_enter("%p,#%d", object, auxdata->len);
 | 
						|
 | 
						|
	/* attempt to install the cache metadata directly */
 | 
						|
	_debug("SET %s #%u", object->fscache.cookie->def->name, auxdata->len);
 | 
						|
 | 
						|
	ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
 | 
						|
			   &auxdata->type, auxdata->len,
 | 
						|
			   XATTR_CREATE);
 | 
						|
	if (ret < 0 && ret != -ENOMEM)
 | 
						|
		cachefiles_io_error_obj(
 | 
						|
			object,
 | 
						|
			"Failed to set xattr with error %d", ret);
 | 
						|
 | 
						|
	_leave(" = %d", ret);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * update the state xattr on a cache file
 | 
						|
 */
 | 
						|
int cachefiles_update_object_xattr(struct cachefiles_object *object,
 | 
						|
				   struct cachefiles_xattr *auxdata)
 | 
						|
{
 | 
						|
	struct dentry *dentry = object->dentry;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ASSERT(object->fscache.cookie);
 | 
						|
	ASSERT(dentry);
 | 
						|
 | 
						|
	_enter("%p,#%d", object, auxdata->len);
 | 
						|
 | 
						|
	/* attempt to install the cache metadata directly */
 | 
						|
	_debug("SET %s #%u", object->fscache.cookie->def->name, auxdata->len);
 | 
						|
 | 
						|
	ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
 | 
						|
			   &auxdata->type, auxdata->len,
 | 
						|
			   XATTR_REPLACE);
 | 
						|
	if (ret < 0 && ret != -ENOMEM)
 | 
						|
		cachefiles_io_error_obj(
 | 
						|
			object,
 | 
						|
			"Failed to update xattr with error %d", ret);
 | 
						|
 | 
						|
	_leave(" = %d", ret);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * check the state xattr on a cache file
 | 
						|
 * - return -ESTALE if the object should be deleted
 | 
						|
 */
 | 
						|
int cachefiles_check_object_xattr(struct cachefiles_object *object,
 | 
						|
				  struct cachefiles_xattr *auxdata)
 | 
						|
{
 | 
						|
	struct cachefiles_xattr *auxbuf;
 | 
						|
	struct dentry *dentry = object->dentry;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	_enter("%p,#%d", object, auxdata->len);
 | 
						|
 | 
						|
	ASSERT(dentry);
 | 
						|
	ASSERT(dentry->d_inode);
 | 
						|
 | 
						|
	auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, cachefiles_gfp);
 | 
						|
	if (!auxbuf) {
 | 
						|
		_leave(" = -ENOMEM");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	/* read the current type label */
 | 
						|
	ret = vfs_getxattr(dentry, cachefiles_xattr_cache,
 | 
						|
			   &auxbuf->type, 512 + 1);
 | 
						|
	if (ret < 0) {
 | 
						|
		if (ret == -ENODATA)
 | 
						|
			goto stale; /* no attribute - power went off
 | 
						|
				     * mid-cull? */
 | 
						|
 | 
						|
		if (ret == -ERANGE)
 | 
						|
			goto bad_type_length;
 | 
						|
 | 
						|
		cachefiles_io_error_obj(object,
 | 
						|
					"Can't read xattr on %lu (err %d)",
 | 
						|
					dentry->d_inode->i_ino, -ret);
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	/* check the on-disk object */
 | 
						|
	if (ret < 1)
 | 
						|
		goto bad_type_length;
 | 
						|
 | 
						|
	if (auxbuf->type != auxdata->type)
 | 
						|
		goto stale;
 | 
						|
 | 
						|
	auxbuf->len = ret;
 | 
						|
 | 
						|
	/* consult the netfs */
 | 
						|
	if (object->fscache.cookie->def->check_aux) {
 | 
						|
		enum fscache_checkaux result;
 | 
						|
		unsigned int dlen;
 | 
						|
 | 
						|
		dlen = auxbuf->len - 1;
 | 
						|
 | 
						|
		_debug("checkaux %s #%u",
 | 
						|
		       object->fscache.cookie->def->name, dlen);
 | 
						|
 | 
						|
		result = fscache_check_aux(&object->fscache,
 | 
						|
					   &auxbuf->data, dlen);
 | 
						|
 | 
						|
		switch (result) {
 | 
						|
			/* entry okay as is */
 | 
						|
		case FSCACHE_CHECKAUX_OKAY:
 | 
						|
			goto okay;
 | 
						|
 | 
						|
			/* entry requires update */
 | 
						|
		case FSCACHE_CHECKAUX_NEEDS_UPDATE:
 | 
						|
			break;
 | 
						|
 | 
						|
			/* entry requires deletion */
 | 
						|
		case FSCACHE_CHECKAUX_OBSOLETE:
 | 
						|
			goto stale;
 | 
						|
 | 
						|
		default:
 | 
						|
			BUG();
 | 
						|
		}
 | 
						|
 | 
						|
		/* update the current label */
 | 
						|
		ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
 | 
						|
				   &auxdata->type, auxdata->len,
 | 
						|
				   XATTR_REPLACE);
 | 
						|
		if (ret < 0) {
 | 
						|
			cachefiles_io_error_obj(object,
 | 
						|
						"Can't update xattr on %lu"
 | 
						|
						" (error %d)",
 | 
						|
						dentry->d_inode->i_ino, -ret);
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
okay:
 | 
						|
	ret = 0;
 | 
						|
 | 
						|
error:
 | 
						|
	kfree(auxbuf);
 | 
						|
	_leave(" = %d", ret);
 | 
						|
	return ret;
 | 
						|
 | 
						|
bad_type_length:
 | 
						|
	kerror("Cache object %lu xattr length incorrect",
 | 
						|
	       dentry->d_inode->i_ino);
 | 
						|
	ret = -EIO;
 | 
						|
	goto error;
 | 
						|
 | 
						|
stale:
 | 
						|
	ret = -ESTALE;
 | 
						|
	goto error;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * remove the object's xattr to mark it stale
 | 
						|
 */
 | 
						|
int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
 | 
						|
				   struct dentry *dentry)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = vfs_removexattr(dentry, cachefiles_xattr_cache);
 | 
						|
	if (ret < 0) {
 | 
						|
		if (ret == -ENOENT || ret == -ENODATA)
 | 
						|
			ret = 0;
 | 
						|
		else if (ret != -ENOMEM)
 | 
						|
			cachefiles_io_error(cache,
 | 
						|
					    "Can't remove xattr from %lu"
 | 
						|
					    " (error %d)",
 | 
						|
					    dentry->d_inode->i_ino, -ret);
 | 
						|
	}
 | 
						|
 | 
						|
	_leave(" = %d", ret);
 | 
						|
	return ret;
 | 
						|
}
 |