Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions fs/fuse/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2043,6 +2043,42 @@ static int fuse_notify_inc_epoch(struct fuse_conn *fc)
return 0;
}

static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size,
struct fuse_copy_state *cs)
{
struct fuse_notify_prune_out outarg;
const unsigned int batch = 512;
u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL);
unsigned int num, i;
int err;

if (!nodeids)
return -ENOMEM;

if (size < sizeof(outarg))
return -EINVAL;

err = fuse_copy_one(cs, &outarg, sizeof(outarg));
if (err)
return err;

if (size - sizeof(outarg) != outarg.count * sizeof(u64))
return -EINVAL;

for (; outarg.count; outarg.count -= num) {
num = min(batch, outarg.count);
err = fuse_copy_one(cs, nodeids, num * sizeof(u64));
if (err)
return err;

scoped_guard(rwsem_read, &fc->killsb) {
for (i = 0; i < num; i++)
fuse_try_prune_one_inode(fc, nodeids[i]);
}
}
return 0;
}

static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
unsigned int size, struct fuse_copy_state *cs)
{
Expand Down Expand Up @@ -2074,6 +2110,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
case FUSE_NOTIFY_INC_EPOCH:
return fuse_notify_inc_epoch(fc);

case FUSE_NOTIFY_PRUNE:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have libfuse interface for FUSE_NOTIFY_PRUNE ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no ... but we have to start at the kernel ... the libfuse code is in a PR

return fuse_notify_prune(fc, size, cs);

default:
fuse_copy_finish(cs);
return -EINVAL;
Expand Down
6 changes: 6 additions & 0 deletions fs/fuse/fuse_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,12 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
u64 child_nodeid, struct qstr *name, u32 flags);

/*
* Try to prune this inode. If neither the inode itself nor dentries associated
* with this inode have any external reference, then the inode can be freed.
*/
void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid);

int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
bool isdir);

Expand Down
21 changes: 21 additions & 0 deletions fs/fuse/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,27 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
return 0;
}

void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid)
{
struct inode *inode;
struct dentry *dentry;

inode = fuse_ilookup(fc, nodeid, NULL);
if (!inode)
return;

if (S_ISDIR(inode->i_mode)) {
dentry = d_find_alias(inode);
if (dentry) {
shrink_dcache_parent(dentry);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this only remove the children which with ref count is zero?

dput(dentry);
}
}

d_prune_aliases(inode);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, only when d_lockref.count is zero. But when lost a lock, we need to invalidate dcache entry

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do at the moment ... that's why I left that code in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think issue with d_invalidate is if it is currently used for for something, which is why Miklos is suggesting shrink_dcache_parent()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the problem with shrink_dcache_parent and `d_prune_aliases is that, if another node delete a file whose dcache already cached in this node, then don't invalidate dcache.

iput(inode);
}

bool fuse_lock_inode(struct inode *inode)
{
bool locked = false;
Expand Down
9 changes: 8 additions & 1 deletion include/uapi/linux/fuse.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
*
* 7.44
* - add FUSE_NOTIFY_INC_EPOCH
* - add FUSE_NOTIFY_PRUNE
*/

#ifndef _LINUX_FUSE_H
Expand Down Expand Up @@ -696,7 +697,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_RESEND = 7,
FUSE_NOTIFY_INC_EPOCH = 8,
FUSE_NOTIFY_CODE_MAX,
FUSE_NOTIFY_PRUNE = 9,
};

/* The read buffer is required to be at least 8k, but may be much larger */
Expand Down Expand Up @@ -1140,6 +1141,12 @@ struct fuse_notify_retrieve_in {
uint64_t dummy4;
};

struct fuse_notify_prune_out {
uint32_t count;
uint32_t padding;
uint64_t spare;
};

struct fuse_backing_map {
int32_t fd;
uint32_t flags;
Expand Down