On Fri, 11 Sep 2009, Jean-Pierre André wrote:
> Hi Miklos,
>
> Thank you for analyzing the results.
>
> Your suspected cause does not look like being the
> right one. I suspected this because the measures
> are related to a quite standard compiling where
> permission checks are not likely to fail.
>
> The extra count (about 1750) must be related to
> checking whether a directory is writeable. There
> were 997 create, 707 unlinks and 42 rename
> (same numbers today).
Good observation. The extra count is not because of checks for
writable, but because these operations invalidate the attribute on the
parent directory (due to size, mtime and nlink changes), and the next
permission check will try to refresh it. This is not necessary,
however, since the mode bits and the owner/group haven't changed.
So the attribute invalidation could refined to tell the difference
when the attributes which determine permission might also have changed
or only ones which don't have any effect on permission checks.
Can you try this patch?
Thanks,
Miklos
Index: linux-2.6/fs/fuse/dir.c
===================================================================
--- linux-2.6.orig/fs/fuse/dir.c 2009-09-14 11:38:03.000000000 +0200
+++ linux-2.6/fs/fuse/dir.c 2009-09-14 12:11:14.000000000 +0200
@@ -89,6 +89,16 @@ void fuse_invalidate_attr(struct inode *
get_fuse_inode(inode)->i_time = 0;
}
+void fuse_invalidate_attr_mask(struct inode *inode, enum fuse_attr_mask attr)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ spin_lock(&fc->lock);
+ fi->attr_invalid |= attr;
+ spin_unlock(&fc->lock);
+}
+
/*
* Just mark the entry as stale, so that a next attempt to look it up
* will result in a new lookup call to userspace
@@ -451,7 +461,7 @@ static int fuse_create_open(struct inode
fuse_put_request(fc, forget_req);
d_instantiate(entry, inode);
fuse_change_entry_timeout(entry, &outentry);
- fuse_invalidate_attr(dir);
+ fuse_invalidate_attr_mask(dir, FUA_DIRCHANGE);
file = lookup_instantiate_filp(nd, entry, generic_file_open);
if (IS_ERR(file)) {
fuse_sync_release(ff, flags);
@@ -534,7 +544,7 @@ static int create_new_entry(struct fuse_
d_instantiate(entry, inode);
fuse_change_entry_timeout(entry, &outarg);
- fuse_invalidate_attr(dir);
+ fuse_invalidate_attr_mask(dir, FUA_DIRCHANGE);
return 0;
out_put_forget_req:
@@ -646,8 +656,8 @@ static int fuse_unlink(struct inode *dir
* lookup/getattr.
*/
clear_nlink(inode);
- fuse_invalidate_attr(inode);
- fuse_invalidate_attr(dir);
+ fuse_invalidate_attr_mask(inode, FUA_TIME | FUA_NLINK);
+ fuse_invalidate_attr_mask(dir, FUA_DIRCHANGE);
fuse_invalidate_entry_cache(entry);
} else if (err == -EINTR)
fuse_invalidate_entry(entry);
@@ -672,7 +682,7 @@ static int fuse_rmdir(struct inode *dir,
fuse_put_request(fc, req);
if (!err) {
clear_nlink(entry->d_inode);
- fuse_invalidate_attr(dir);
+ fuse_invalidate_attr_mask(dir, FUA_DIRCHANGE);
fuse_invalidate_entry_cache(entry);
} else if (err == -EINTR)
fuse_invalidate_entry(entry);
@@ -705,11 +715,11 @@ static int fuse_rename(struct inode *old
fuse_put_request(fc, req);
if (!err) {
/* ctime changes */
- fuse_invalidate_attr(oldent->d_inode);
+ fuse_invalidate_attr_mask(oldent->d_inode, FUA_TIME);
- fuse_invalidate_attr(olddir);
+ fuse_invalidate_attr_mask(olddir, FUA_DIRCHANGE);
if (olddir != newdir)
- fuse_invalidate_attr(newdir);
+ fuse_invalidate_attr_mask(newdir, FUA_DIRCHANGE);
/* newent will end up negative */
if (newent->d_inode)
@@ -755,7 +765,7 @@ static int fuse_link(struct dentry *entr
etc.)
*/
if (!err || err == -EINTR)
- fuse_invalidate_attr(inode);
+ fuse_invalidate_attr_mask(inode, FUA_NLINK | FUA_TIME);
return err;
}
@@ -835,13 +845,14 @@ static int fuse_do_getattr(struct inode
}
int fuse_update_attributes(struct inode *inode, struct kstat *stat,
- struct file *file, bool *refreshed)
+ struct file *file, bool *refreshed,
+ enum fuse_attr_mask mask)
{
struct fuse_inode *fi = get_fuse_inode(inode);
int err;
bool r;
- if (fi->i_time < get_jiffies_64()) {
+ if (fi->i_time < get_jiffies_64() || (fi->attr_invalid & mask)) {
r = true;
err = fuse_do_getattr(inode, stat, file);
} else {
@@ -990,7 +1001,7 @@ static int fuse_permission(struct inode
*/
if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
- err = fuse_update_attributes(inode, NULL, NULL, &refreshed);
+ err = fuse_update_attributes(inode, NULL, NULL, &refreshed, 0);
if (err)
return err;
}
@@ -1085,7 +1096,7 @@ static int fuse_readdir(struct file *fil
filldir);
__free_page(page);
- fuse_invalidate_attr(inode); /* atime changed */
+ fuse_invalidate_attr_mask(inode, FUA_TIME);
return err;
}
@@ -1118,7 +1129,7 @@ static char *read_link(struct dentry *de
link[req->out.args[0].size] = '\0';
out:
fuse_put_request(fc, req);
- fuse_invalidate_attr(inode); /* atime changed */
+ fuse_invalidate_attr_mask(inode, FUA_TIME);
return link;
}
@@ -1381,7 +1392,7 @@ static int fuse_getattr(struct vfsmount
if (!fuse_allow_task(fc, current))
return -EACCES;
- return fuse_update_attributes(inode, stat, NULL, NULL);
+ return fuse_update_attributes(inode, stat, NULL, NULL, FUA_ALL);
}
static int fuse_setxattr(struct dentry *entry, const char *name,
Index: linux-2.6/fs/fuse/fuse_i.h
===================================================================
--- linux-2.6.orig/fs/fuse/fuse_i.h 2009-09-14 11:38:03.000000000 +0200
+++ linux-2.6/fs/fuse/fuse_i.h 2009-09-14 12:10:42.000000000 +0200
@@ -55,6 +55,14 @@ extern struct list_head fuse_conn_list;
/** Global mutex protecting fuse_conn_list and the control filesystem */
extern struct mutex fuse_mutex;
+enum fuse_attr_mask {
+ FUA_SIZE = (1 << 0),
+ FUA_NLINK = (1 << 1),
+ FUA_TIME = (1 << 2),
+ FUA_DIRCHANGE = FUA_NLINK | FUA_SIZE | FUA_TIME,
+ FUA_ALL = ~0U,
+};
+
/** FUSE inode */
struct fuse_inode {
/** Inode data */
@@ -80,6 +88,9 @@ struct fuse_inode {
/** Version of last attribute change */
u64 attr_version;
+ /** Mask indicating which attributes may be invalid */
+ enum fuse_attr_mask attr_invalid;
+
/** Files usable in writepage. Protected by fc->lock */
struct list_head write_files;
@@ -671,6 +682,8 @@ void fuse_abort_conn(struct fuse_conn *f
*/
void fuse_invalidate_attr(struct inode *inode);
+void fuse_invalidate_attr_mask(struct inode *inode, enum fuse_attr_mask attr);
+
void fuse_invalidate_entry_cache(struct dentry *entry);
/**
@@ -713,7 +726,8 @@ int fuse_allow_task(struct fuse_conn *fc
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
int fuse_update_attributes(struct inode *inode, struct kstat *stat,
- struct file *file, bool *refreshed);
+ struct file *file, bool *refreshed,
+ enum fuse_attr_mask mask);
void fuse_flush_writepages(struct inode *inode);
Index: linux-2.6/fs/fuse/inode.c
===================================================================
--- linux-2.6.orig/fs/fuse/inode.c 2009-09-14 11:38:03.000000000 +0200
+++ linux-2.6/fs/fuse/inode.c 2009-09-14 11:38:08.000000000 +0200
@@ -131,6 +131,7 @@ void fuse_change_attributes_common(struc
fi->attr_version = ++fc->attr_version;
fi->i_time = attr_valid;
+ fi->attr_invalid = 0;
inode->i_ino = attr->ino;
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
Index: linux-2.6/fs/fuse/file.c
===================================================================
--- linux-2.6.orig/fs/fuse/file.c 2009-09-14 11:38:03.000000000 +0200
+++ linux-2.6/fs/fuse/file.c 2009-09-14 11:38:08.000000000 +0200
@@ -506,7 +506,7 @@ static int fuse_readpage(struct file *fi
SetPageUptodate(page);
}
- fuse_invalidate_attr(inode); /* atime changed */
+ fuse_invalidate_attr_mask(inode, FUA_TIME);
out:
unlock_page(page);
return err;
@@ -527,7 +527,7 @@ static void fuse_readpages_end(struct fu
fuse_read_update_size(inode, pos, req->misc.read.attr_ver);
}
- fuse_invalidate_attr(inode); /* atime changed */
+ fuse_invalidate_attr_mask(inode, FUA_TIME);
for (i = 0; i < req->num_pages; i++) {
struct page *page = req->pages[i];
@@ -635,7 +635,8 @@ static ssize_t fuse_file_aio_read(struct
* If trying to read past EOF, make sure the i_size
* attribute is up-to-date.
*/
- err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL);
+ err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL,
+ FUA_SIZE);
if (err)
return err;
}
@@ -744,7 +745,7 @@ static int fuse_buffered_write(struct fi
if (count == PAGE_CACHE_SIZE)
SetPageUptodate(page);
}
- fuse_invalidate_attr(inode);
+ fuse_invalidate_attr_mask(inode, FUA_TIME | FUA_SIZE);
return err ? err : nres;
}
@@ -905,7 +906,7 @@ static ssize_t fuse_perform_write(struct
if (res > 0)
fuse_write_update_size(inode, pos);
- fuse_invalidate_attr(inode);
+ fuse_invalidate_attr_mask(inode, FUA_TIME | FUA_SIZE);
return res > 0 ? res : err;
}
@@ -1082,7 +1083,7 @@ static ssize_t fuse_direct_read(struct f
res = fuse_direct_io(file, buf, count, ppos, 0);
- fuse_invalidate_attr(inode);
+ fuse_invalidate_attr_mask(inode, FUA_TIME);
return res;
}
@@ -1106,7 +1107,7 @@ static ssize_t fuse_direct_write(struct
}
mutex_unlock(&inode->i_mutex);
- fuse_invalidate_attr(inode);
+ fuse_invalidate_attr_mask(inode, FUA_TIME | FUA_SIZE);
return res;
}
@@ -1541,7 +1542,8 @@ static loff_t fuse_file_llseek(struct fi
mutex_lock(&inode->i_mutex);
switch (origin) {
case SEEK_END:
- retval = fuse_update_attributes(inode, NULL, file, NULL);
+ retval = fuse_update_attributes(inode, NULL, file, NULL,
+ FUA_SIZE);
if (retval)
goto exit;
offset += i_size_read(inode);
|