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 \
 | 	cell.o \
 | ||||||
| 	cmservice.o \
 | 	cmservice.o \
 | ||||||
| 	dir.o \
 | 	dir.o \
 | ||||||
|  | 	dir_edit.o \
 | ||||||
| 	dynroot.o \
 | 	dynroot.o \
 | ||||||
| 	file.o \
 | 	file.o \
 | ||||||
| 	flock.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); | 	qty /= sizeof(union afs_xdr_dir_block); | ||||||
| 
 | 
 | ||||||
| 	/* check them */ | 	/* check them */ | ||||||
| 	dbuf = page_address(page); | 	dbuf = kmap(page); | ||||||
| 	for (tmp = 0; tmp < qty; tmp++) { | 	for (tmp = 0; tmp < qty; tmp++) { | ||||||
| 		if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { | 		if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { | ||||||
| 			printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", | 			printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", | ||||||
| 			       __func__, dvnode->vfs_inode.i_ino, tmp, qty, | 			       __func__, dvnode->vfs_inode.i_ino, tmp, qty, | ||||||
| 			       ntohs(dbuf->blocks[tmp].hdr.magic)); | 			       ntohs(dbuf->blocks[tmp].hdr.magic)); | ||||||
| 			trace_afs_dir_check_failed(dvnode, off, i_size); | 			trace_afs_dir_check_failed(dvnode, off, i_size); | ||||||
|  | 			kunmap(page); | ||||||
| 			goto error; | 			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: | checked: | ||||||
| 	afs_stat_v(dvnode, n_read_dir); | 	afs_stat_v(dvnode, n_read_dir); | ||||||
| 	return true; | 	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_vnode *dvnode = AFS_FS_I(dir); | ||||||
| 	struct afs_fid newfid; | 	struct afs_fid newfid; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 data_version = dvnode->status.data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mode |= S_IFDIR; | 	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)) { | 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||||
| 		while (afs_select_fileserver(&fc)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			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); | 				      &newfid, &newstatus, &newcb); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -1145,6 +1155,11 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | ||||||
| 		goto error_key; | 		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); | 	key_put(key); | ||||||
| 	_leave(" = 0"); | 	_leave(" = 0"); | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -1168,6 +1183,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry) | ||||||
| 		clear_nlink(&vnode->vfs_inode); | 		clear_nlink(&vnode->vfs_inode); | ||||||
| 		set_bit(AFS_VNODE_DELETED, &vnode->flags); | 		set_bit(AFS_VNODE_DELETED, &vnode->flags); | ||||||
| 		clear_bit(AFS_VNODE_CB_PROMISED, &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_fs_cursor fc; | ||||||
| 	struct afs_vnode *dvnode = AFS_FS_I(dir); | 	struct afs_vnode *dvnode = AFS_FS_I(dir); | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 data_version = dvnode->status.data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	_enter("{%x:%u},{%pd}", | 	_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)) { | 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||||
| 		while (afs_select_fileserver(&fc)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			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); | 		afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||||||
| 		ret = afs_end_vnode_operation(&fc); | 		ret = afs_end_vnode_operation(&fc); | ||||||
| 		if (ret == 0) | 		if (ret == 0) { | ||||||
| 			afs_dir_remove_subdir(dentry); | 			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); | 	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 afs_vnode *dvnode = AFS_FS_I(dir), *vnode; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
| 	unsigned long d_version = (unsigned long)dentry->d_fsdata; | 	unsigned long d_version = (unsigned long)dentry->d_fsdata; | ||||||
|  | 	u64 data_version = dvnode->status.data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	_enter("{%x:%u},{%pd}", | 	_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)) { | 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||||
| 		while (afs_select_fileserver(&fc)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			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); | 		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( | 			ret = afs_dir_remove_link( | ||||||
| 				dentry, key, d_version, | 				dentry, key, d_version, | ||||||
| 				(unsigned long)dvnode->status.data_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: | 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_vnode *dvnode = AFS_FS_I(dir); | ||||||
| 	struct afs_fid newfid; | 	struct afs_fid newfid; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 data_version = dvnode->status.data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mode |= S_IFREG; | 	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)) { | 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||||
| 		while (afs_select_fileserver(&fc)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			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); | 				      &newfid, &newstatus, &newcb); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -1356,6 +1385,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | ||||||
| 		goto error_key; | 		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); | 	key_put(key); | ||||||
| 	_leave(" = 0"); | 	_leave(" = 0"); | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -1377,10 +1410,12 @@ static int afs_link(struct dentry *from, struct inode *dir, | ||||||
| 	struct afs_fs_cursor fc; | 	struct afs_fs_cursor fc; | ||||||
| 	struct afs_vnode *dvnode, *vnode; | 	struct afs_vnode *dvnode, *vnode; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	vnode = AFS_FS_I(d_inode(from)); | 	vnode = AFS_FS_I(d_inode(from)); | ||||||
| 	dvnode = AFS_FS_I(dir); | 	dvnode = AFS_FS_I(dir); | ||||||
|  | 	data_version = dvnode->status.data_version; | ||||||
| 
 | 
 | ||||||
| 	_enter("{%x:%u},{%x:%u},{%pd}", | 	_enter("{%x:%u},{%x:%u},{%pd}", | ||||||
| 	       vnode->fid.vid, vnode->fid.vnode, | 	       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)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | ||||||
| 			fc.cb_break_2 = vnode->cb_break + vnode->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); | 		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; | 		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); | 	key_put(key); | ||||||
| 	_leave(" = 0"); | 	_leave(" = 0"); | ||||||
| 	return 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_vnode *dvnode = AFS_FS_I(dir); | ||||||
| 	struct afs_fid newfid; | 	struct afs_fid newfid; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 data_version = dvnode->status.data_version; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	_enter("{%x:%u},{%pd},%s", | 	_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)) { | 	if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||||||
| 		while (afs_select_fileserver(&fc)) { | 		while (afs_select_fileserver(&fc)) { | ||||||
| 			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 			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); | 				       &newfid, &newstatus); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -1484,6 +1525,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | ||||||
| 		goto error_key; | 		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); | 	key_put(key); | ||||||
| 	_leave(" = 0"); | 	_leave(" = 0"); | ||||||
| 	return 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_fs_cursor fc; | ||||||
| 	struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | 	struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | ||||||
| 	struct key *key; | 	struct key *key; | ||||||
|  | 	u64 orig_data_version, new_data_version; | ||||||
|  | 	bool new_negative = d_is_negative(new_dentry); | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (flags) | 	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)); | 	vnode = AFS_FS_I(d_inode(old_dentry)); | ||||||
| 	orig_dvnode = AFS_FS_I(old_dir); | 	orig_dvnode = AFS_FS_I(old_dir); | ||||||
| 	new_dvnode = AFS_FS_I(new_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}", | 	_enter("{%x:%u},{%x:%u},{%x:%u},{%pd}", | ||||||
| 	       orig_dvnode->fid.vid, orig_dvnode->fid.vnode, | 	       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 = orig_dvnode->cb_break + orig_dvnode->cb_s_break; | ||||||
| 			fc.cb_break_2 = new_dvnode->cb_break + new_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, | 			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); | 		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; | 			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: | error_key: | ||||||
| 	key_put(key); | 	key_put(key); | ||||||
| error: | 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 { | 			} else { | ||||||
| 				set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); | 				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_lo); | ||||||
| 	size |= (u64)ntohl(xdr->size_hi) << 32; | 	size |= (u64)ntohl(xdr->size_hi) << 32; | ||||||
| 	if (size != status->size) { | 	status->size = size; | ||||||
| 		status->size = size; |  | ||||||
| 		flags |= AFS_VNODE_DATA_CHANGED; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	data_version  = (u64)ntohl(xdr->data_version_lo); | 	data_version  = (u64)ntohl(xdr->data_version_lo); | ||||||
| 	data_version |= (u64)ntohl(xdr->data_version_hi) << 32; | 	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, | int afs_fs_create(struct afs_fs_cursor *fc, | ||||||
| 		  const char *name, | 		  const char *name, | ||||||
| 		  umode_t mode, | 		  umode_t mode, | ||||||
|  | 		  u64 current_data_version, | ||||||
| 		  struct afs_fid *newfid, | 		  struct afs_fid *newfid, | ||||||
| 		  struct afs_file_status *newstatus, | 		  struct afs_file_status *newstatus, | ||||||
| 		  struct afs_callback *newcb) | 		  struct afs_callback *newcb) | ||||||
|  | @ -763,7 +768,7 @@ int afs_fs_create(struct afs_fs_cursor *fc, | ||||||
| 	call->reply[1] = newfid; | 	call->reply[1] = newfid; | ||||||
| 	call->reply[2] = newstatus; | 	call->reply[2] = newstatus; | ||||||
| 	call->reply[3] = newcb; | 	call->reply[3] = newcb; | ||||||
| 	call->expected_version = vnode->status.data_version; | 	call->expected_version = current_data_version + 1; | ||||||
| 
 | 
 | ||||||
| 	/* marshall the parameters */ | 	/* marshall the parameters */ | ||||||
| 	bp = call->request; | 	bp = call->request; | ||||||
|  | @ -836,7 +841,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = { | ||||||
| /*
 | /*
 | ||||||
|  * remove a file or directory |  * 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_vnode *vnode = fc->vnode; | ||||||
| 	struct afs_call *call; | 	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->key = fc->key; | ||||||
| 	call->reply[0] = vnode; | 	call->reply[0] = vnode; | ||||||
| 	call->expected_version = vnode->status.data_version; | 	call->expected_version = current_data_version + 1; | ||||||
| 
 | 
 | ||||||
| 	/* marshall the parameters */ | 	/* marshall the parameters */ | ||||||
| 	bp = call->request; | 	bp = call->request; | ||||||
|  | @ -920,7 +926,7 @@ static const struct afs_call_type afs_RXFSLink = { | ||||||
|  * make a hard link |  * make a hard link | ||||||
|  */ |  */ | ||||||
| int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, | 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_vnode *dvnode = fc->vnode; | ||||||
| 	struct afs_call *call; | 	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->key = fc->key; | ||||||
| 	call->reply[0] = dvnode; | 	call->reply[0] = dvnode; | ||||||
| 	call->reply[1] = vnode; | 	call->reply[1] = vnode; | ||||||
| 	call->expected_version = vnode->status.data_version; | 	call->expected_version = current_data_version + 1; | ||||||
| 
 | 
 | ||||||
| 	/* marshall the parameters */ | 	/* marshall the parameters */ | ||||||
| 	bp = call->request; | 	bp = call->request; | ||||||
|  | @ -1009,6 +1015,7 @@ static const struct afs_call_type afs_RXFSSymlink = { | ||||||
| int afs_fs_symlink(struct afs_fs_cursor *fc, | int afs_fs_symlink(struct afs_fs_cursor *fc, | ||||||
| 		   const char *name, | 		   const char *name, | ||||||
| 		   const char *contents, | 		   const char *contents, | ||||||
|  | 		   u64 current_data_version, | ||||||
| 		   struct afs_fid *newfid, | 		   struct afs_fid *newfid, | ||||||
| 		   struct afs_file_status *newstatus) | 		   struct afs_file_status *newstatus) | ||||||
| { | { | ||||||
|  | @ -1037,7 +1044,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc, | ||||||
| 	call->reply[0] = vnode; | 	call->reply[0] = vnode; | ||||||
| 	call->reply[1] = newfid; | 	call->reply[1] = newfid; | ||||||
| 	call->reply[2] = newstatus; | 	call->reply[2] = newstatus; | ||||||
| 	call->expected_version = vnode->status.data_version; | 	call->expected_version = current_data_version + 1; | ||||||
| 
 | 
 | ||||||
| 	/* marshall the parameters */ | 	/* marshall the parameters */ | ||||||
| 	bp = call->request; | 	bp = call->request; | ||||||
|  | @ -1117,7 +1124,9 @@ static const struct afs_call_type afs_RXFSRename = { | ||||||
| int afs_fs_rename(struct afs_fs_cursor *fc, | int afs_fs_rename(struct afs_fs_cursor *fc, | ||||||
| 		  const char *orig_name, | 		  const char *orig_name, | ||||||
| 		  struct afs_vnode *new_dvnode, | 		  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_vnode *orig_dvnode = fc->vnode; | ||||||
| 	struct afs_call *call; | 	struct afs_call *call; | ||||||
|  | @ -1145,8 +1154,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc, | ||||||
| 	call->key = fc->key; | 	call->key = fc->key; | ||||||
| 	call->reply[0] = orig_dvnode; | 	call->reply[0] = orig_dvnode; | ||||||
| 	call->reply[1] = new_dvnode; | 	call->reply[1] = new_dvnode; | ||||||
| 	call->expected_version = orig_dvnode->status.data_version; | 	call->expected_version = current_orig_data_version + 1; | ||||||
| 	call->expected_version_2 = new_dvnode->status.data_version; | 	call->expected_version_2 = current_new_data_version + 1; | ||||||
| 
 | 
 | ||||||
| 	/* marshall the parameters */ | 	/* marshall the parameters */ | ||||||
| 	bp = call->request; | 	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 (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | ||||||
| 		if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { | 		if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { | ||||||
| 			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) && | 		} else if (vnode->status.type == AFS_FTYPE_DIR && | ||||||
| 			   !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && | 			   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) { | 			   vnode->cb_expires_at - 10 > now) { | ||||||
| 				valid = true; | 				valid = true; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -271,6 +271,8 @@ struct afs_net { | ||||||
| 	atomic_t		n_inval;	/* Number of invalidations by the server */ | 	atomic_t		n_inval;	/* Number of invalidations by the server */ | ||||||
| 	atomic_t		n_relpg;	/* Number of invalidations by releasepage */ | 	atomic_t		n_relpg;	/* Number of invalidations by releasepage */ | ||||||
| 	atomic_t		n_read_dir;	/* Number of directory pages read */ | 	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[]; | 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 *); | 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 |  * 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_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_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_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 *); | 			 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_remove(struct afs_fs_cursor *, const char *, bool, u64); | ||||||
| extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *); | 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 *, | extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64, | ||||||
| 			  struct afs_fid *, struct afs_file_status *); | 			  struct afs_fid *, struct afs_file_status *); | ||||||
| extern int afs_fs_rename(struct afs_fs_cursor *, const char *, | 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 *, | extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *, | ||||||
| 			     pgoff_t, pgoff_t, unsigned, unsigned); | 			     pgoff_t, pgoff_t, unsigned, unsigned); | ||||||
| extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *); | 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", | 	seq_printf(m, "dir-data: rdpg=%u\n", | ||||||
| 		   atomic_read(&net->n_read_dir)); | 		   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; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -63,6 +63,27 @@ enum afs_vl_operation { | ||||||
| 	afs_VL_GetCapabilities	= 65537,	/* AFS Get VL server capabilities */ | 	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 */ | #endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */ | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -106,6 +127,25 @@ enum afs_vl_operation { | ||||||
| 	EM(afs_YFSVL_GetEndpoints,		"YFSVL.GetEndpoints") \ | 	EM(afs_YFSVL_GetEndpoints,		"YFSVL.GetEndpoints") \ | ||||||
| 	E_(afs_VL_GetCapabilities,		"VL.GetCapabilities") | 	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. |  * Export enum symbols via userspace. | ||||||
|  | @ -118,6 +158,8 @@ enum afs_vl_operation { | ||||||
| afs_call_traces; | afs_call_traces; | ||||||
| afs_fs_operations; | afs_fs_operations; | ||||||
| afs_vl_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 |  * 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) | 		      __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 */ | #endif /* _TRACE_AFS_H */ | ||||||
| 
 | 
 | ||||||
| /* This part must be outside protection */ | /* This part must be outside protection */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 David Howells
						David Howells