forked from mirrors/linux
		
	afs: Locally edit directory data for mkdir/create/unlink/...
Locally edit the contents of an AFS directory upon a successful inode operation that modifies that directory (such as mkdir, create and unlink) so that we can avoid the current practice of re-downloading the directory after each change. This is viable provided that the directory version number we get back from the modifying RPC op is exactly incremented by 1 from what we had previously. The data in the directory contents is in a defined format that we have to parse locally to perform lookups and readdir, so modifying isn't a problem. If the edit fails, we just clear the VALID flag on the directory and it will be reloaded next time it is needed. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
		
							parent
							
								
									0031763698
								
							
						
					
					
						commit
						63a4681ff3
					
				
					 8 changed files with 715 additions and 29 deletions
				
			
		|  | @ -12,6 +12,7 @@ kafs-objs := \ | |||
| 	cell.o \
 | ||||
| 	cmservice.o \
 | ||||
| 	dir.o \
 | ||||
| 	dir_edit.o \
 | ||||
| 	dynroot.o \
 | ||||
| 	file.o \
 | ||||
| 	flock.o \
 | ||||
|  |  | |||
							
								
								
									
										83
									
								
								fs/afs/dir.c
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								fs/afs/dir.c
									
									
									
									
									
								
							|  | @ -130,17 +130,26 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, | |||
| 	qty /= sizeof(union afs_xdr_dir_block); | ||||
| 
 | ||||
| 	/* check them */ | ||||
| 	dbuf = page_address(page); | ||||
| 	dbuf = kmap(page); | ||||
| 	for (tmp = 0; tmp < qty; tmp++) { | ||||
| 		if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { | ||||
| 			printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", | ||||
| 			       __func__, dvnode->vfs_inode.i_ino, tmp, qty, | ||||
| 			       ntohs(dbuf->blocks[tmp].hdr.magic)); | ||||
| 			trace_afs_dir_check_failed(dvnode, off, i_size); | ||||
| 			kunmap(page); | ||||
| 			goto error; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Make sure each block is NUL terminated so we can reasonably
 | ||||
| 		 * use string functions on it.  The filenames in the page | ||||
| 		 * *should* be NUL-terminated anyway. | ||||
| 		 */ | ||||
| 		((u8 *)&dbuf->blocks[tmp])[AFS_DIR_BLOCK_SIZE - 1] = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	kunmap(page); | ||||
| 
 | ||||
| checked: | ||||
| 	afs_stat_v(dvnode, n_read_dir); | ||||
| 	return true; | ||||
|  | @ -1114,6 +1123,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir); | ||||
| 	struct afs_fid newfid; | ||||
| 	struct key *key; | ||||
| 	u64 data_version = dvnode->status.data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mode |= S_IFDIR; | ||||
|  | @ -1131,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
| 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			afs_fs_create(&fc, dentry->d_name.name, mode, | ||||
| 			afs_fs_create(&fc, dentry->d_name.name, mode, data_version, | ||||
| 				      &newfid, &newstatus, &newcb); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1145,6 +1155,11 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
| 		goto error_key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret == 0 && | ||||
| 	    test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 		afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||||
| 				 afs_edit_dir_for_create); | ||||
| 
 | ||||
| 	key_put(key); | ||||
| 	_leave(" = 0"); | ||||
| 	return 0; | ||||
|  | @ -1168,6 +1183,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry) | |||
| 		clear_nlink(&vnode->vfs_inode); | ||||
| 		set_bit(AFS_VNODE_DELETED, &vnode->flags); | ||||
| 		clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | ||||
| 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1179,6 +1195,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
| 	struct afs_fs_cursor fc; | ||||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir); | ||||
| 	struct key *key; | ||||
| 	u64 data_version = dvnode->status.data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	_enter("{%x:%u},{%pd}", | ||||
|  | @ -1194,13 +1211,18 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
| 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			afs_fs_remove(&fc, dentry->d_name.name, true); | ||||
| 			afs_fs_remove(&fc, dentry->d_name.name, true, | ||||
| 				      data_version); | ||||
| 		} | ||||
| 
 | ||||
| 		afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||||
| 		ret = afs_end_vnode_operation(&fc); | ||||
| 		if (ret == 0) | ||||
| 		if (ret == 0) { | ||||
| 			afs_dir_remove_subdir(dentry); | ||||
| 			if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 				afs_edit_dir_remove(dvnode, &dentry->d_name, | ||||
| 						    afs_edit_dir_for_rmdir); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	key_put(key); | ||||
|  | @ -1265,6 +1287,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; | ||||
| 	struct key *key; | ||||
| 	unsigned long d_version = (unsigned long)dentry->d_fsdata; | ||||
| 	u64 data_version = dvnode->status.data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	_enter("{%x:%u},{%pd}", | ||||
|  | @ -1291,7 +1314,8 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			afs_fs_remove(&fc, dentry->d_name.name, false); | ||||
| 			afs_fs_remove(&fc, dentry->d_name.name, false, | ||||
| 				      data_version); | ||||
| 		} | ||||
| 
 | ||||
| 		afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||||
|  | @ -1300,6 +1324,10 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
| 			ret = afs_dir_remove_link( | ||||
| 				dentry, key, d_version, | ||||
| 				(unsigned long)dvnode->status.data_version); | ||||
| 		if (ret == 0 && | ||||
| 		    test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 			afs_edit_dir_remove(dvnode, &dentry->d_name, | ||||
| 					    afs_edit_dir_for_unlink); | ||||
| 	} | ||||
| 
 | ||||
| error_key: | ||||
|  | @ -1321,6 +1349,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir); | ||||
| 	struct afs_fid newfid; | ||||
| 	struct key *key; | ||||
| 	u64 data_version = dvnode->status.data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mode |= S_IFREG; | ||||
|  | @ -1342,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
| 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			afs_fs_create(&fc, dentry->d_name.name, mode, | ||||
| 			afs_fs_create(&fc, dentry->d_name.name, mode, data_version, | ||||
| 				      &newfid, &newstatus, &newcb); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1356,6 +1385,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
| 		goto error_key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 		afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||||
| 				 afs_edit_dir_for_create); | ||||
| 
 | ||||
| 	key_put(key); | ||||
| 	_leave(" = 0"); | ||||
| 	return 0; | ||||
|  | @ -1377,10 +1410,12 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
| 	struct afs_fs_cursor fc; | ||||
| 	struct afs_vnode *dvnode, *vnode; | ||||
| 	struct key *key; | ||||
| 	u64 data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	vnode = AFS_FS_I(d_inode(from)); | ||||
| 	dvnode = AFS_FS_I(dir); | ||||
| 	data_version = dvnode->status.data_version; | ||||
| 
 | ||||
| 	_enter("{%x:%u},{%x:%u},{%pd}", | ||||
| 	       vnode->fid.vid, vnode->fid.vnode, | ||||
|  | @ -1407,7 +1442,7 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break; | ||||
| 			afs_fs_link(&fc, vnode, dentry->d_name.name); | ||||
| 			afs_fs_link(&fc, vnode, dentry->d_name.name, data_version); | ||||
| 		} | ||||
| 
 | ||||
| 		afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||||
|  | @ -1423,6 +1458,10 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
| 		goto error_key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 		afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid, | ||||
| 				 afs_edit_dir_for_link); | ||||
| 
 | ||||
| 	key_put(key); | ||||
| 	_leave(" = 0"); | ||||
| 	return 0; | ||||
|  | @ -1446,6 +1485,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir); | ||||
| 	struct afs_fid newfid; | ||||
| 	struct key *key; | ||||
| 	u64 data_version = dvnode->status.data_version; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	_enter("{%x:%u},{%pd},%s", | ||||
|  | @ -1470,7 +1510,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
| 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||
| 		while (afs_select_fileserver(&fc)) { | ||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||
| 			afs_fs_symlink(&fc, dentry->d_name.name, content, | ||||
| 			afs_fs_symlink(&fc, dentry->d_name.name, | ||||
| 				       content, data_version, | ||||
| 				       &newfid, &newstatus); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1484,6 +1525,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
| 		goto error_key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||||
| 		afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||||
| 				 afs_edit_dir_for_symlink); | ||||
| 
 | ||||
| 	key_put(key); | ||||
| 	_leave(" = 0"); | ||||
| 	return 0; | ||||
|  | @ -1506,6 +1551,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 	struct afs_fs_cursor fc; | ||||
| 	struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | ||||
| 	struct key *key; | ||||
| 	u64 orig_data_version, new_data_version; | ||||
| 	bool new_negative = d_is_negative(new_dentry); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (flags) | ||||
|  | @ -1514,6 +1561,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 	vnode = AFS_FS_I(d_inode(old_dentry)); | ||||
| 	orig_dvnode = AFS_FS_I(old_dir); | ||||
| 	new_dvnode = AFS_FS_I(new_dir); | ||||
| 	orig_data_version = orig_dvnode->status.data_version; | ||||
| 	new_data_version = new_dvnode->status.data_version; | ||||
| 
 | ||||
| 	_enter("{%x:%u},{%x:%u},{%x:%u},{%pd}", | ||||
| 	       orig_dvnode->fid.vid, orig_dvnode->fid.vnode, | ||||
|  | @ -1539,7 +1588,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 			fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break; | ||||
| 			fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break; | ||||
| 			afs_fs_rename(&fc, old_dentry->d_name.name, | ||||
| 				      new_dvnode, new_dentry->d_name.name); | ||||
| 				      new_dvnode, new_dentry->d_name.name, | ||||
| 				      orig_data_version, new_data_version); | ||||
| 		} | ||||
| 
 | ||||
| 		afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break); | ||||
|  | @ -1551,6 +1601,21 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 			goto error_key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret == 0) { | ||||
| 		if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags)) | ||||
| 		    afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, | ||||
| 					afs_edit_dir_for_rename); | ||||
| 
 | ||||
| 		if (!new_negative && | ||||
| 		    test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | ||||
| 			afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, | ||||
| 					    afs_edit_dir_for_rename); | ||||
| 
 | ||||
| 		if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | ||||
| 			afs_edit_dir_add(new_dvnode, &new_dentry->d_name, | ||||
| 					 &vnode->fid,  afs_edit_dir_for_rename); | ||||
| 	} | ||||
| 
 | ||||
| error_key: | ||||
| 	key_put(key); | ||||
| error: | ||||
|  |  | |||
							
								
								
									
										505
									
								
								fs/afs/dir_edit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								fs/afs/dir_edit.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,505 @@ | |||
| /* AFS filesystem directory editing
 | ||||
|  * | ||||
|  * Copyright (C) 2018 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/kernel.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/namei.h> | ||||
| #include <linux/pagemap.h> | ||||
| #include <linux/iversion.h> | ||||
| #include "internal.h" | ||||
| #include "xdr_fs.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * Find a number of contiguous clear bits in a directory block bitmask. | ||||
|  * | ||||
|  * There are 64 slots, which means we can load the entire bitmap into a | ||||
|  * variable.  The first bit doesn't count as it corresponds to the block header | ||||
|  * slot.  nr_slots is between 1 and 9. | ||||
|  */ | ||||
| static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots) | ||||
| { | ||||
| 	u64 bitmap; | ||||
| 	u32 mask; | ||||
| 	int bit, n; | ||||
| 
 | ||||
| 	bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; | ||||
| 	bitmap >>= 1; /* The first entry is metadata */ | ||||
| 	bit = 1; | ||||
| 	mask = (1 << nr_slots) - 1; | ||||
| 
 | ||||
| 	do { | ||||
| 		if (sizeof(unsigned long) == 8) | ||||
| 			n = ffz(bitmap); | ||||
| 		else | ||||
| 			n = ((u32)bitmap) != 0 ? | ||||
| 				ffz((u32)bitmap) : | ||||
| 				ffz((u32)(bitmap >> 32)) + 32; | ||||
| 		bitmap >>= n; | ||||
| 		bit += n; | ||||
| 
 | ||||
| 		if ((bitmap & mask) == 0) { | ||||
| 			if (bit > 64 - nr_slots) | ||||
| 				return -1; | ||||
| 			return bit; | ||||
| 		} | ||||
| 
 | ||||
| 		n = __ffs(bitmap); | ||||
| 		bitmap >>= n; | ||||
| 		bit += n; | ||||
| 	} while (bitmap); | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Set a number of contiguous bits in the directory block bitmap. | ||||
|  */ | ||||
| static void afs_set_contig_bits(union afs_xdr_dir_block *block, | ||||
| 				int bit, unsigned int nr_slots) | ||||
| { | ||||
| 	u64 mask, before, after; | ||||
| 
 | ||||
| 	mask = (1 << nr_slots) - 1; | ||||
| 	mask <<= bit; | ||||
| 
 | ||||
| 	before = *(u64 *)block->hdr.bitmap; | ||||
| 
 | ||||
| 	block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8); | ||||
| 	block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8); | ||||
| 	block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8); | ||||
| 	block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8); | ||||
| 	block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8); | ||||
| 	block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8); | ||||
| 	block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8); | ||||
| 	block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8); | ||||
| 
 | ||||
| 	after = *(u64 *)block->hdr.bitmap; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Clear a number of contiguous bits in the directory block bitmap. | ||||
|  */ | ||||
| static void afs_clear_contig_bits(union afs_xdr_dir_block *block, | ||||
| 				  int bit, unsigned int nr_slots) | ||||
| { | ||||
| 	u64 mask, before, after; | ||||
| 
 | ||||
| 	mask = (1 << nr_slots) - 1; | ||||
| 	mask <<= bit; | ||||
| 
 | ||||
| 	before = *(u64 *)block->hdr.bitmap; | ||||
| 
 | ||||
| 	block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8); | ||||
| 	block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8); | ||||
| 	block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8); | ||||
| 	block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8); | ||||
| 	block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8); | ||||
| 	block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8); | ||||
| 	block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8); | ||||
| 	block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8); | ||||
| 
 | ||||
| 	after = *(u64 *)block->hdr.bitmap; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Scan a directory block looking for a dirent of the right name. | ||||
|  */ | ||||
| static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name, | ||||
| 			      unsigned int blocknum) | ||||
| { | ||||
| 	union afs_xdr_dirent *de; | ||||
| 	u64 bitmap; | ||||
| 	int d, len, n; | ||||
| 
 | ||||
| 	_enter(""); | ||||
| 
 | ||||
| 	bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; | ||||
| 	bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; | ||||
| 
 | ||||
| 	for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS); | ||||
| 	     d < AFS_DIR_SLOTS_PER_BLOCK; | ||||
| 	     d++) { | ||||
| 		if (!((bitmap >> d) & 1)) | ||||
| 			continue; | ||||
| 		de = &block->dirents[d]; | ||||
| 		if (de->u.valid != 1) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* The block was NUL-terminated by afs_dir_check_page(). */ | ||||
| 		len = strlen(de->u.name); | ||||
| 		if (len == name->len && | ||||
| 		    memcmp(de->u.name, name->name, name->len) == 0) | ||||
| 			return d; | ||||
| 
 | ||||
| 		n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||||
| 		n /= AFS_DIR_DIRENT_SIZE; | ||||
| 		d += n - 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialise a new directory block.  Note that block 0 is special and contains | ||||
|  * some extra metadata. | ||||
|  */ | ||||
| static void afs_edit_init_block(union afs_xdr_dir_block *meta, | ||||
| 				union afs_xdr_dir_block *block, int block_num) | ||||
| { | ||||
| 	memset(block, 0, sizeof(*block)); | ||||
| 	block->hdr.npages = htons(1); | ||||
| 	block->hdr.magic = AFS_DIR_MAGIC; | ||||
| 	block->hdr.bitmap[0] = 1; | ||||
| 
 | ||||
| 	if (block_num == 0) { | ||||
| 		block->hdr.bitmap[0] = 0xff; | ||||
| 		block->hdr.bitmap[1] = 0x1f; | ||||
| 		memset(block->meta.alloc_ctrs, | ||||
| 		       AFS_DIR_SLOTS_PER_BLOCK, | ||||
| 		       sizeof(block->meta.alloc_ctrs)); | ||||
| 		meta->meta.alloc_ctrs[0] = | ||||
| 			AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (block_num < AFS_DIR_BLOCKS_WITH_CTR) | ||||
| 		meta->meta.alloc_ctrs[block_num] = | ||||
| 			AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Edit a directory's file data to add a new directory entry.  Doing this after | ||||
|  * create, mkdir, symlink, link or rename if the data version number is | ||||
|  * incremented by exactly one avoids the need to re-download the entire | ||||
|  * directory contents. | ||||
|  * | ||||
|  * The caller must hold the inode locked. | ||||
|  */ | ||||
| void afs_edit_dir_add(struct afs_vnode *vnode, | ||||
| 		      struct qstr *name, struct afs_fid *new_fid, | ||||
| 		      enum afs_edit_dir_reason why) | ||||
| { | ||||
| 	union afs_xdr_dir_block *meta, *block; | ||||
| 	struct afs_xdr_dir_page *meta_page, *dir_page; | ||||
| 	union afs_xdr_dirent *de; | ||||
| 	struct page *page0, *page; | ||||
| 	unsigned int need_slots, nr_blocks, b; | ||||
| 	pgoff_t index; | ||||
| 	loff_t i_size; | ||||
| 	gfp_t gfp; | ||||
| 	int slot; | ||||
| 
 | ||||
| 	_enter(",,{%d,%s},", name->len, name->name); | ||||
| 
 | ||||
| 	i_size = i_size_read(&vnode->vfs_inode); | ||||
| 	if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || | ||||
| 	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { | ||||
| 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	gfp = vnode->vfs_inode.i_mapping->gfp_mask; | ||||
| 	page0 = find_or_create_page(vnode->vfs_inode.i_mapping, 0, gfp); | ||||
| 	if (!page0) { | ||||
| 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 		_leave(" [fgp]"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Work out how many slots we're going to need. */ | ||||
| 	need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||||
| 	need_slots /= AFS_DIR_DIRENT_SIZE; | ||||
| 
 | ||||
| 	meta_page = kmap(page0); | ||||
| 	meta = &meta_page->blocks[0]; | ||||
| 	if (i_size == 0) | ||||
| 		goto new_directory; | ||||
| 	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; | ||||
| 
 | ||||
| 	/* Find a block that has sufficient slots available.  Each VM page
 | ||||
| 	 * contains two or more directory blocks. | ||||
| 	 */ | ||||
| 	for (b = 0; b < nr_blocks + 1; b++) { | ||||
| 		/* If the directory extended into a new page, then we need to
 | ||||
| 		 * tack a new page on the end. | ||||
| 		 */ | ||||
| 		index = b / AFS_DIR_BLOCKS_PER_PAGE; | ||||
| 		if (index == 0) { | ||||
| 			page = page0; | ||||
| 			dir_page = meta_page; | ||||
| 		} else { | ||||
| 			if (nr_blocks >= AFS_DIR_MAX_BLOCKS) | ||||
| 				goto error; | ||||
| 			gfp = vnode->vfs_inode.i_mapping->gfp_mask; | ||||
| 			page = find_or_create_page(vnode->vfs_inode.i_mapping, | ||||
| 						   index, gfp); | ||||
| 			if (!page) | ||||
| 				goto error; | ||||
| 			if (!PagePrivate(page)) { | ||||
| 				set_page_private(page, 1); | ||||
| 				SetPagePrivate(page); | ||||
| 			} | ||||
| 			dir_page = kmap(page); | ||||
| 		} | ||||
| 
 | ||||
| 		/* Abandon the edit if we got a callback break. */ | ||||
| 		if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||||
| 			goto invalidated; | ||||
| 
 | ||||
| 		block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE]; | ||||
| 
 | ||||
| 		_debug("block %u: %2u %3u %u", | ||||
| 		       b, | ||||
| 		       (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99, | ||||
| 		       ntohs(block->hdr.npages), | ||||
| 		       ntohs(block->hdr.magic)); | ||||
| 
 | ||||
| 		/* Initialise the block if necessary. */ | ||||
| 		if (b == nr_blocks) { | ||||
| 			_debug("init %u", b); | ||||
| 			afs_edit_init_block(meta, block, b); | ||||
| 			i_size_write(&vnode->vfs_inode, (b + 1) * AFS_DIR_BLOCK_SIZE); | ||||
| 		} | ||||
| 
 | ||||
| 		/* Only lower dir pages have a counter in the header. */ | ||||
| 		if (b >= AFS_DIR_BLOCKS_WITH_CTR || | ||||
| 		    meta->meta.alloc_ctrs[b] >= need_slots) { | ||||
| 			/* We need to try and find one or more consecutive
 | ||||
| 			 * slots to hold the entry. | ||||
| 			 */ | ||||
| 			slot = afs_find_contig_bits(block, need_slots); | ||||
| 			if (slot >= 0) { | ||||
| 				_debug("slot %u", slot); | ||||
| 				goto found_space; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (page != page0) { | ||||
| 			unlock_page(page); | ||||
| 			kunmap(page); | ||||
| 			put_page(page); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* There are no spare slots of sufficient size, yet the operation
 | ||||
| 	 * succeeded.  Download the directory again. | ||||
| 	 */ | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	goto out_unmap; | ||||
| 
 | ||||
| new_directory: | ||||
| 	afs_edit_init_block(meta, meta, 0); | ||||
| 	i_size = AFS_DIR_BLOCK_SIZE; | ||||
| 	i_size_write(&vnode->vfs_inode, i_size); | ||||
| 	slot = AFS_DIR_RESV_BLOCKS0; | ||||
| 	page = page0; | ||||
| 	block = meta; | ||||
| 	nr_blocks = 1; | ||||
| 	b = 0; | ||||
| 
 | ||||
| found_space: | ||||
| 	/* Set the dirent slot. */ | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot, | ||||
| 			   new_fid->vnode, new_fid->unique, name->name); | ||||
| 	de = &block->dirents[slot]; | ||||
| 	de->u.valid	= 1; | ||||
| 	de->u.unused[0]	= 0; | ||||
| 	de->u.hash_next	= 0; // TODO: Really need to maintain this
 | ||||
| 	de->u.vnode	= htonl(new_fid->vnode); | ||||
| 	de->u.unique	= htonl(new_fid->unique); | ||||
| 	memcpy(de->u.name, name->name, name->len + 1); | ||||
| 	de->u.name[name->len] = 0; | ||||
| 
 | ||||
| 	/* Adjust the bitmap. */ | ||||
| 	afs_set_contig_bits(block, slot, need_slots); | ||||
| 	if (page != page0) { | ||||
| 		unlock_page(page); | ||||
| 		kunmap(page); | ||||
| 		put_page(page); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Adjust the allocation counter. */ | ||||
| 	if (b < AFS_DIR_BLOCKS_WITH_CTR) | ||||
| 		meta->meta.alloc_ctrs[b] -= need_slots; | ||||
| 
 | ||||
| 	inode_inc_iversion_raw(&vnode->vfs_inode); | ||||
| 	afs_stat_v(vnode, n_dir_cr); | ||||
| 	_debug("Insert %s in %u[%u]", name->name, b, slot); | ||||
| 
 | ||||
| out_unmap: | ||||
| 	unlock_page(page0); | ||||
| 	kunmap(page0); | ||||
| 	put_page(page0); | ||||
| 	_leave(""); | ||||
| 	return; | ||||
| 
 | ||||
| invalidated: | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	if (page != page0) { | ||||
| 		kunmap(page); | ||||
| 		put_page(page); | ||||
| 	} | ||||
| 	goto out_unmap; | ||||
| 
 | ||||
| error: | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	goto out_unmap; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Edit a directory's file data to remove a new directory entry.  Doing this | ||||
|  * after unlink, rmdir or rename if the data version number is incremented by | ||||
|  * exactly one avoids the need to re-download the entire directory contents. | ||||
|  * | ||||
|  * The caller must hold the inode locked. | ||||
|  */ | ||||
| void afs_edit_dir_remove(struct afs_vnode *vnode, | ||||
| 			 struct qstr *name, enum afs_edit_dir_reason why) | ||||
| { | ||||
| 	struct afs_xdr_dir_page *meta_page, *dir_page; | ||||
| 	union afs_xdr_dir_block *meta, *block; | ||||
| 	union afs_xdr_dirent *de; | ||||
| 	struct page *page0, *page; | ||||
| 	unsigned int need_slots, nr_blocks, b; | ||||
| 	pgoff_t index; | ||||
| 	loff_t i_size; | ||||
| 	int slot; | ||||
| 
 | ||||
| 	_enter(",,{%d,%s},", name->len, name->name); | ||||
| 
 | ||||
| 	i_size = i_size_read(&vnode->vfs_inode); | ||||
| 	if (i_size < AFS_DIR_BLOCK_SIZE || | ||||
| 	    i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || | ||||
| 	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { | ||||
| 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 		return; | ||||
| 	} | ||||
| 	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; | ||||
| 
 | ||||
| 	page0 = find_lock_page(vnode->vfs_inode.i_mapping, 0); | ||||
| 	if (!page0) { | ||||
| 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 		_leave(" [fgp]"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Work out how many slots we're going to discard. */ | ||||
| 	need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||||
| 	need_slots /= AFS_DIR_DIRENT_SIZE; | ||||
| 
 | ||||
| 	meta_page = kmap(page0); | ||||
| 	meta = &meta_page->blocks[0]; | ||||
| 
 | ||||
| 	/* Find a page that has sufficient slots available.  Each VM page
 | ||||
| 	 * contains two or more directory blocks. | ||||
| 	 */ | ||||
| 	for (b = 0; b < nr_blocks; b++) { | ||||
| 		index = b / AFS_DIR_BLOCKS_PER_PAGE; | ||||
| 		if (index != 0) { | ||||
| 			page = find_lock_page(vnode->vfs_inode.i_mapping, index); | ||||
| 			if (!page) | ||||
| 				goto error; | ||||
| 			dir_page = kmap(page); | ||||
| 		} else { | ||||
| 			page = page0; | ||||
| 			dir_page = meta_page; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Abandon the edit if we got a callback break. */ | ||||
| 		if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||||
| 			goto invalidated; | ||||
| 
 | ||||
| 		block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE]; | ||||
| 
 | ||||
| 		if (b > AFS_DIR_BLOCKS_WITH_CTR || | ||||
| 		    meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) { | ||||
| 			slot = afs_dir_scan_block(block, name, b); | ||||
| 			if (slot >= 0) | ||||
| 				goto found_dirent; | ||||
| 		} | ||||
| 
 | ||||
| 		if (page != page0) { | ||||
| 			unlock_page(page); | ||||
| 			kunmap(page); | ||||
| 			put_page(page); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Didn't find the dirent to clobber.  Download the directory again. */ | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent, | ||||
| 			   0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	goto out_unmap; | ||||
| 
 | ||||
| found_dirent: | ||||
| 	de = &block->dirents[slot]; | ||||
| 
 | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot, | ||||
| 			   ntohl(de->u.vnode), ntohl(de->u.unique), | ||||
| 			   name->name); | ||||
| 
 | ||||
| 	memset(de, 0, sizeof(*de) * need_slots); | ||||
| 
 | ||||
| 	/* Adjust the bitmap. */ | ||||
| 	afs_clear_contig_bits(block, slot, need_slots); | ||||
| 	if (page != page0) { | ||||
| 		unlock_page(page); | ||||
| 		kunmap(page); | ||||
| 		put_page(page); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Adjust the allocation counter. */ | ||||
| 	if (b < AFS_DIR_BLOCKS_WITH_CTR) | ||||
| 		meta->meta.alloc_ctrs[b] += need_slots; | ||||
| 
 | ||||
| 	inode_set_iversion_raw(&vnode->vfs_inode, vnode->status.data_version); | ||||
| 	afs_stat_v(vnode, n_dir_rm); | ||||
| 	_debug("Remove %s from %u[%u]", name->name, b, slot); | ||||
| 
 | ||||
| out_unmap: | ||||
| 	unlock_page(page0); | ||||
| 	kunmap(page0); | ||||
| 	put_page(page0); | ||||
| 	_leave(""); | ||||
| 	return; | ||||
| 
 | ||||
| invalidated: | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval, | ||||
| 			   0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	if (page != page0) { | ||||
| 		unlock_page(page); | ||||
| 		kunmap(page); | ||||
| 		put_page(page); | ||||
| 	} | ||||
| 	goto out_unmap; | ||||
| 
 | ||||
| error: | ||||
| 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error, | ||||
| 			   0, 0, 0, 0, name->name); | ||||
| 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||||
| 	goto out_unmap; | ||||
| } | ||||
|  | @ -107,6 +107,13 @@ void afs_update_inode_from_status(struct afs_vnode *vnode, | |||
| 			} else { | ||||
| 				set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); | ||||
| 			} | ||||
| 		} else if (vnode->status.type == AFS_FTYPE_DIR) { | ||||
| 			/* Expected directory change is handled elsewhere so
 | ||||
| 			 * that we can locally edit the directory and save on a | ||||
| 			 * download. | ||||
| 			 */ | ||||
| 			if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||||
| 				flags &= ~AFS_VNODE_DATA_CHANGED; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -190,10 +197,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, | |||
| 
 | ||||
| 	size  = (u64)ntohl(xdr->size_lo); | ||||
| 	size |= (u64)ntohl(xdr->size_hi) << 32; | ||||
| 	if (size != status->size) { | ||||
| 		status->size = size; | ||||
| 		flags |= AFS_VNODE_DATA_CHANGED; | ||||
| 	} | ||||
| 	status->size = size; | ||||
| 
 | ||||
| 	data_version  = (u64)ntohl(xdr->data_version_lo); | ||||
| 	data_version |= (u64)ntohl(xdr->data_version_hi) << 32; | ||||
|  | @ -736,6 +740,7 @@ static const struct afs_call_type afs_RXFSMakeDir = { | |||
| int afs_fs_create(struct afs_fs_cursor *fc, | ||||
| 		  const char *name, | ||||
| 		  umode_t mode, | ||||
| 		  u64 current_data_version, | ||||
| 		  struct afs_fid *newfid, | ||||
| 		  struct afs_file_status *newstatus, | ||||
| 		  struct afs_callback *newcb) | ||||
|  | @ -763,7 +768,7 @@ int afs_fs_create(struct afs_fs_cursor *fc, | |||
| 	call->reply[1] = newfid; | ||||
| 	call->reply[2] = newstatus; | ||||
| 	call->reply[3] = newcb; | ||||
| 	call->expected_version = vnode->status.data_version; | ||||
| 	call->expected_version = current_data_version + 1; | ||||
| 
 | ||||
| 	/* marshall the parameters */ | ||||
| 	bp = call->request; | ||||
|  | @ -836,7 +841,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = { | |||
| /*
 | ||||
|  * remove a file or directory | ||||
|  */ | ||||
| int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir) | ||||
| int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir, | ||||
| 		  u64 current_data_version) | ||||
| { | ||||
| 	struct afs_vnode *vnode = fc->vnode; | ||||
| 	struct afs_call *call; | ||||
|  | @ -858,7 +864,7 @@ int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir) | |||
| 
 | ||||
| 	call->key = fc->key; | ||||
| 	call->reply[0] = vnode; | ||||
| 	call->expected_version = vnode->status.data_version; | ||||
| 	call->expected_version = current_data_version + 1; | ||||
| 
 | ||||
| 	/* marshall the parameters */ | ||||
| 	bp = call->request; | ||||
|  | @ -920,7 +926,7 @@ static const struct afs_call_type afs_RXFSLink = { | |||
|  * make a hard link | ||||
|  */ | ||||
| int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, | ||||
| 		const char *name) | ||||
| 		const char *name, u64 current_data_version) | ||||
| { | ||||
| 	struct afs_vnode *dvnode = fc->vnode; | ||||
| 	struct afs_call *call; | ||||
|  | @ -941,7 +947,7 @@ int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, | |||
| 	call->key = fc->key; | ||||
| 	call->reply[0] = dvnode; | ||||
| 	call->reply[1] = vnode; | ||||
| 	call->expected_version = vnode->status.data_version; | ||||
| 	call->expected_version = current_data_version + 1; | ||||
| 
 | ||||
| 	/* marshall the parameters */ | ||||
| 	bp = call->request; | ||||
|  | @ -1009,6 +1015,7 @@ static const struct afs_call_type afs_RXFSSymlink = { | |||
| int afs_fs_symlink(struct afs_fs_cursor *fc, | ||||
| 		   const char *name, | ||||
| 		   const char *contents, | ||||
| 		   u64 current_data_version, | ||||
| 		   struct afs_fid *newfid, | ||||
| 		   struct afs_file_status *newstatus) | ||||
| { | ||||
|  | @ -1037,7 +1044,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc, | |||
| 	call->reply[0] = vnode; | ||||
| 	call->reply[1] = newfid; | ||||
| 	call->reply[2] = newstatus; | ||||
| 	call->expected_version = vnode->status.data_version; | ||||
| 	call->expected_version = current_data_version + 1; | ||||
| 
 | ||||
| 	/* marshall the parameters */ | ||||
| 	bp = call->request; | ||||
|  | @ -1117,7 +1124,9 @@ static const struct afs_call_type afs_RXFSRename = { | |||
| int afs_fs_rename(struct afs_fs_cursor *fc, | ||||
| 		  const char *orig_name, | ||||
| 		  struct afs_vnode *new_dvnode, | ||||
| 		  const char *new_name) | ||||
| 		  const char *new_name, | ||||
| 		  u64 current_orig_data_version, | ||||
| 		  u64 current_new_data_version) | ||||
| { | ||||
| 	struct afs_vnode *orig_dvnode = fc->vnode; | ||||
| 	struct afs_call *call; | ||||
|  | @ -1145,8 +1154,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc, | |||
| 	call->key = fc->key; | ||||
| 	call->reply[0] = orig_dvnode; | ||||
| 	call->reply[1] = new_dvnode; | ||||
| 	call->expected_version = orig_dvnode->status.data_version; | ||||
| 	call->expected_version_2 = new_dvnode->status.data_version; | ||||
| 	call->expected_version = current_orig_data_version + 1; | ||||
| 	call->expected_version_2 = current_new_data_version + 1; | ||||
| 
 | ||||
| 	/* marshall the parameters */ | ||||
| 	bp = call->request; | ||||
|  |  | |||
|  | @ -395,8 +395,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) | |||
| 	if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | ||||
| 		if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { | ||||
| 			vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; | ||||
| 		} else if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && | ||||
| 			   !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && | ||||
| 		} else if (vnode->status.type == AFS_FTYPE_DIR && | ||||
| 			   test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && | ||||
| 			   vnode->cb_expires_at - 10 > now) { | ||||
| 				valid = true; | ||||
| 		} else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && | ||||
| 			   vnode->cb_expires_at - 10 > now) { | ||||
| 				valid = true; | ||||
| 		} | ||||
|  |  | |||
|  | @ -271,6 +271,8 @@ struct afs_net { | |||
| 	atomic_t		n_inval;	/* Number of invalidations by the server */ | ||||
| 	atomic_t		n_relpg;	/* Number of invalidations by releasepage */ | ||||
| 	atomic_t		n_read_dir;	/* Number of directory pages read */ | ||||
| 	atomic_t		n_dir_cr;	/* Number of directory entry creation edits */ | ||||
| 	atomic_t		n_dir_rm;	/* Number of directory entry removal edits */ | ||||
| }; | ||||
| 
 | ||||
| extern const char afs_init_sysname[]; | ||||
|  | @ -679,6 +681,13 @@ extern const struct dentry_operations afs_fs_dentry_operations; | |||
| 
 | ||||
| extern void afs_d_release(struct dentry *); | ||||
| 
 | ||||
| /*
 | ||||
|  * dir_edit.c | ||||
|  */ | ||||
| extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *, | ||||
| 			     enum afs_edit_dir_reason); | ||||
| extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason); | ||||
| 
 | ||||
| /*
 | ||||
|  * dynroot.c | ||||
|  */ | ||||
|  | @ -725,14 +734,14 @@ extern void afs_update_inode_from_status(struct afs_vnode *, struct afs_file_sta | |||
| extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool); | ||||
| extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *); | ||||
| extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *); | ||||
| extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, | ||||
| extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, u64, | ||||
| 			 struct afs_fid *, struct afs_file_status *, struct afs_callback *); | ||||
| extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool); | ||||
| extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *); | ||||
| extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, | ||||
| extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool, u64); | ||||
| extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64); | ||||
| extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64, | ||||
| 			  struct afs_fid *, struct afs_file_status *); | ||||
| extern int afs_fs_rename(struct afs_fs_cursor *, const char *, | ||||
| 			 struct afs_vnode *, const char *); | ||||
| 			 struct afs_vnode *, const char *, u64, u64); | ||||
| extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *, | ||||
| 			     pgoff_t, pgoff_t, unsigned, unsigned); | ||||
| extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *); | ||||
|  |  | |||
|  | @ -918,6 +918,10 @@ static int afs_proc_stats_show(struct seq_file *m, void *v) | |||
| 
 | ||||
| 	seq_printf(m, "dir-data: rdpg=%u\n", | ||||
| 		   atomic_read(&net->n_read_dir)); | ||||
| 
 | ||||
| 	seq_printf(m, "dir-edit: cr=%u rm=%u\n", | ||||
| 		   atomic_read(&net->n_dir_cr), | ||||
| 		   atomic_read(&net->n_dir_rm)); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,6 +63,27 @@ enum afs_vl_operation { | |||
| 	afs_VL_GetCapabilities	= 65537,	/* AFS Get VL server capabilities */ | ||||
| }; | ||||
| 
 | ||||
| enum afs_edit_dir_op { | ||||
| 	afs_edit_dir_create, | ||||
| 	afs_edit_dir_create_error, | ||||
| 	afs_edit_dir_create_inval, | ||||
| 	afs_edit_dir_create_nospc, | ||||
| 	afs_edit_dir_delete, | ||||
| 	afs_edit_dir_delete_error, | ||||
| 	afs_edit_dir_delete_inval, | ||||
| 	afs_edit_dir_delete_noent, | ||||
| }; | ||||
| 
 | ||||
| enum afs_edit_dir_reason { | ||||
| 	afs_edit_dir_for_create, | ||||
| 	afs_edit_dir_for_link, | ||||
| 	afs_edit_dir_for_mkdir, | ||||
| 	afs_edit_dir_for_rename, | ||||
| 	afs_edit_dir_for_rmdir, | ||||
| 	afs_edit_dir_for_symlink, | ||||
| 	afs_edit_dir_for_unlink, | ||||
| }; | ||||
| 
 | ||||
| #endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */ | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -106,6 +127,25 @@ enum afs_vl_operation { | |||
| 	EM(afs_YFSVL_GetEndpoints,		"YFSVL.GetEndpoints") \ | ||||
| 	E_(afs_VL_GetCapabilities,		"VL.GetCapabilities") | ||||
| 
 | ||||
| #define afs_edit_dir_ops				  \ | ||||
| 	EM(afs_edit_dir_create,			"create") \ | ||||
| 	EM(afs_edit_dir_create_error,		"c_fail") \ | ||||
| 	EM(afs_edit_dir_create_inval,		"c_invl") \ | ||||
| 	EM(afs_edit_dir_create_nospc,		"c_nspc") \ | ||||
| 	EM(afs_edit_dir_delete,			"delete") \ | ||||
| 	EM(afs_edit_dir_delete_error,		"d_err ") \ | ||||
| 	EM(afs_edit_dir_delete_inval,		"d_invl") \ | ||||
| 	E_(afs_edit_dir_delete_noent,		"d_nent") | ||||
| 
 | ||||
| #define afs_edit_dir_reasons				  \ | ||||
| 	EM(afs_edit_dir_for_create,		"Create") \ | ||||
| 	EM(afs_edit_dir_for_link,		"Link  ") \ | ||||
| 	EM(afs_edit_dir_for_mkdir,		"MkDir ") \ | ||||
| 	EM(afs_edit_dir_for_rename,		"Rename") \ | ||||
| 	EM(afs_edit_dir_for_rmdir,		"RmDir ") \ | ||||
| 	EM(afs_edit_dir_for_symlink,		"Symlnk") \ | ||||
| 	E_(afs_edit_dir_for_unlink,		"Unlink") | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Export enum symbols via userspace. | ||||
|  | @ -118,6 +158,8 @@ enum afs_vl_operation { | |||
| afs_call_traces; | ||||
| afs_fs_operations; | ||||
| afs_vl_operations; | ||||
| afs_edit_dir_ops; | ||||
| afs_edit_dir_reasons; | ||||
| 
 | ||||
| /*
 | ||||
|  * Now redefine the EM() and E_() macros to map the enums to the strings that | ||||
|  | @ -464,6 +506,54 @@ TRACE_EVENT(afs_call_state, | |||
| 		      __entry->ret, __entry->abort) | ||||
| 	    ); | ||||
| 
 | ||||
| TRACE_EVENT(afs_edit_dir, | ||||
| 	    TP_PROTO(struct afs_vnode *dvnode, | ||||
| 		     enum afs_edit_dir_reason why, | ||||
| 		     enum afs_edit_dir_op op, | ||||
| 		     unsigned int block, | ||||
| 		     unsigned int slot, | ||||
| 		     unsigned int f_vnode, | ||||
| 		     unsigned int f_unique, | ||||
| 		     const char *name), | ||||
| 
 | ||||
| 	    TP_ARGS(dvnode, why, op, block, slot, f_vnode, f_unique, name), | ||||
| 
 | ||||
| 	    TP_STRUCT__entry( | ||||
| 		    __field(unsigned int,		vnode		) | ||||
| 		    __field(unsigned int,		unique		) | ||||
| 		    __field(enum afs_edit_dir_reason,	why		) | ||||
| 		    __field(enum afs_edit_dir_op,	op		) | ||||
| 		    __field(unsigned int,		block		) | ||||
| 		    __field(unsigned short,		slot		) | ||||
| 		    __field(unsigned int,		f_vnode		) | ||||
| 		    __field(unsigned int,		f_unique	) | ||||
| 		    __array(char,			name, 18	) | ||||
| 			     ), | ||||
| 
 | ||||
| 	    TP_fast_assign( | ||||
| 		    int __len = strlen(name); | ||||
| 		    __len = min(__len, 17); | ||||
| 		    __entry->vnode	= dvnode->fid.vnode; | ||||
| 		    __entry->unique	= dvnode->fid.unique; | ||||
| 		    __entry->why	= why; | ||||
| 		    __entry->op		= op; | ||||
| 		    __entry->block	= block; | ||||
| 		    __entry->slot	= slot; | ||||
| 		    __entry->f_vnode	= f_vnode; | ||||
| 		    __entry->f_unique	= f_unique; | ||||
| 		    memcpy(__entry->name, name, __len); | ||||
| 		    __entry->name[__len] = 0; | ||||
| 			   ), | ||||
| 
 | ||||
| 	    TP_printk("d=%x:%x %s %s %u[%u] f=%x:%x %s", | ||||
| 		      __entry->vnode, __entry->unique, | ||||
| 		      __print_symbolic(__entry->why, afs_edit_dir_reasons), | ||||
| 		      __print_symbolic(__entry->op, afs_edit_dir_ops), | ||||
| 		      __entry->block, __entry->slot, | ||||
| 		      __entry->f_vnode, __entry->f_unique, | ||||
| 		      __entry->name) | ||||
| 	    ); | ||||
| 
 | ||||
| #endif /* _TRACE_AFS_H */ | ||||
| 
 | ||||
| /* This part must be outside protection */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 David Howells
						David Howells