Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fuse: add prune notification

Some fuse servers need to prune their caches, which can only be done if the
kernel's own dentry/inode caches are pruned first to avoid dangling
references.

Add FUSE_NOTIFY_PRUNE, which takes an array of node ID's to try and get rid
of. Inodes with active references are skipped.

A similar functionality is already provided by FUSE_NOTIFY_INVAL_ENTRY with
the FUSE_EXPIRE_ONLY flag. Differences in the interface are

FUSE_NOTIFY_INVAL_ENTRY:

- can only prune one dentry

- dentry is determined by parent ID and name

- if inode has multiple aliases (cached hard links), then they would have
to be invalidated individually to be able to get rid of the inode

FUSE_NOTIFY_PRUNE:

- can prune multiple inodes

- inodes determined by their node ID

- aliases are taken care of automatically

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

+64
+39
fs/fuse/dev.c
··· 2034 2034 return 0; 2035 2035 } 2036 2036 2037 + static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, 2038 + struct fuse_copy_state *cs) 2039 + { 2040 + struct fuse_notify_prune_out outarg; 2041 + const unsigned int batch = 512; 2042 + u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL); 2043 + unsigned int num, i; 2044 + int err; 2045 + 2046 + if (!nodeids) 2047 + return -ENOMEM; 2048 + 2049 + if (size < sizeof(outarg)) 2050 + return -EINVAL; 2051 + 2052 + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); 2053 + if (err) 2054 + return err; 2055 + 2056 + if (size - sizeof(outarg) != outarg.count * sizeof(u64)) 2057 + return -EINVAL; 2058 + 2059 + for (; outarg.count; outarg.count -= num) { 2060 + num = min(batch, outarg.count); 2061 + err = fuse_copy_one(cs, nodeids, num * sizeof(u64)); 2062 + if (err) 2063 + return err; 2064 + 2065 + scoped_guard(rwsem_read, &fc->killsb) { 2066 + for (i = 0; i < num; i++) 2067 + fuse_try_prune_one_inode(fc, nodeids[i]); 2068 + } 2069 + } 2070 + return 0; 2071 + } 2072 + 2037 2073 static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, 2038 2074 unsigned int size, struct fuse_copy_state *cs) 2039 2075 { ··· 2100 2064 2101 2065 case FUSE_NOTIFY_INC_EPOCH: 2102 2066 return fuse_notify_inc_epoch(fc); 2067 + 2068 + case FUSE_NOTIFY_PRUNE: 2069 + return fuse_notify_prune(fc, size, cs); 2103 2070 2104 2071 default: 2105 2072 return -EINVAL;
+6
fs/fuse/fuse_i.h
··· 1413 1413 int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, 1414 1414 u64 child_nodeid, struct qstr *name, u32 flags); 1415 1415 1416 + /* 1417 + * Try to prune this inode. If neither the inode itself nor dentries associated 1418 + * with this inode have any external reference, then the inode can be freed. 1419 + */ 1420 + void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid); 1421 + 1416 1422 int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, 1417 1423 bool isdir); 1418 1424
+11
fs/fuse/inode.c
··· 585 585 return 0; 586 586 } 587 587 588 + void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid) 589 + { 590 + struct inode *inode; 591 + 592 + inode = fuse_ilookup(fc, nodeid, NULL); 593 + if (!inode) 594 + return; 595 + d_prune_aliases(inode); 596 + iput(inode); 597 + } 598 + 588 599 bool fuse_lock_inode(struct inode *inode) 589 600 { 590 601 bool locked = false;
+8
include/uapi/linux/fuse.h
··· 239 239 * 7.45 240 240 * - add FUSE_COPY_FILE_RANGE_64 241 241 * - add struct fuse_copy_file_range_out 242 + * - add FUSE_NOTIFY_PRUNE 242 243 */ 243 244 244 245 #ifndef _LINUX_FUSE_H ··· 681 680 FUSE_NOTIFY_DELETE = 6, 682 681 FUSE_NOTIFY_RESEND = 7, 683 682 FUSE_NOTIFY_INC_EPOCH = 8, 683 + FUSE_NOTIFY_PRUNE = 9, 684 684 }; 685 685 686 686 /* The read buffer is required to be at least 8k, but may be much larger */ ··· 1118 1116 uint32_t dummy2; 1119 1117 uint64_t dummy3; 1120 1118 uint64_t dummy4; 1119 + }; 1120 + 1121 + struct fuse_notify_prune_out { 1122 + uint32_t count; 1123 + uint32_t padding; 1124 + uint64_t spare; 1121 1125 }; 1122 1126 1123 1127 struct fuse_backing_map {