return 0;
}
+static int dentry_lru_show(struct seq_file *s, void *ptr)
+{
+ struct ceph_client *client = s->private;
+ struct ceph_mds_client *mdsc = &client->mdsc;
+ struct list_head *p;
+ struct ceph_dentry_info *di;
+
+ spin_lock(&mdsc->dentry_lru_lock);
+ list_for_each(p, &mdsc->dentry_lru) {
+ struct dentry *dentry;
+ di = list_entry(p, struct ceph_dentry_info, lru);
+ dentry = di->dentry;
+ seq_printf(s, "%p %p\t%.*s\n",
+ di, dentry, dentry->d_name.len, dentry->d_name.name);
+ }
+ spin_unlock(&mdsc->dentry_lru_lock);
+
+ return 0;
+}
+
#define DEFINE_SHOW_FUNC(name) \
static int name##_open(struct inode *inode, struct file *file) \
{ \
DEFINE_SHOW_FUNC(monc_show)
DEFINE_SHOW_FUNC(mdsc_show)
DEFINE_SHOW_FUNC(osdc_show)
+DEFINE_SHOW_FUNC(dentry_lru_show)
#ifdef CONFIG_CEPH_BOOKKEEPER
static int debugfs_bookkeeper_set(void *data, u64 val)
if (!client->debugfs_osdmap)
goto out;
+ client->debugfs_dentry_lru = debugfs_create_file("dentry_lru",
+ 0600,
+ client->debugfs_dir,
+ client,
+ &dentry_lru_show_fops);
+ if (!client->debugfs_osdmap)
+ goto out;
+
return 0;
out:
debugfs_remove(client->monc.debugfs_file);
debugfs_remove(client->mdsc.debugfs_file);
debugfs_remove(client->osdc.debugfs_file);
+ debugfs_remove(client->debugfs_dentry_lru);
debugfs_remove(client->debugfs_monmap);
debugfs_remove(client->debugfs_mdsmap);
debugfs_remove(client->debugfs_osdmap);
spin_lock(&dentry->d_lock);
di = ceph_dentry(dentry);
- if (di) {
+ if (di && di->lease_session) {
s = di->lease_session;
spin_lock(&s->s_cap_lock);
gen = s->s_cap_gen;
if (ceph_snap(dir) != CEPH_NOSNAP) {
dout(10, "d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
- return 1;
+ goto out_touch;
}
if (dentry_lease_is_valid(dentry))
- return 1;
+ goto out_touch;
if (dir_lease_is_valid(dir, dentry))
- return 1;
+ goto out_touch;
dout(20, "dentry_revalidate %p invalid, clearing %p complete\n",
dentry, dir);
ceph_i_clear(dir, CEPH_I_COMPLETE|CEPH_I_READDIR);
d_drop(dentry);
return 0;
+out_touch:
+ ceph_dentry_lru_touch(dentry);
+ return 1;
}
static void ceph_dentry_release(struct dentry *dentry)
struct inode *parent_inode = dentry->d_parent->d_inode;
if (di) {
- ceph_put_mds_session(di->lease_session);
+ ceph_dentry_lru_del(dentry);
+ if (di->lease_session)
+ ceph_put_mds_session(di->lease_session);
kfree(di);
dentry->d_fsdata = NULL;
}
return ret;
}
+void ceph_dentry_lru_add(struct dentry *dn)
+{
+ struct ceph_dentry_info *di = ceph_dentry(dn);
+ struct ceph_mds_client *mdsc;
+ dout(30, "dentry_lru_add %p %p\t%.*s\n",
+ di, dn, dn->d_name.len, dn->d_name.name);
+
+ if (di) {
+ mdsc = &ceph_client(dn->d_sb)->mdsc;
+ spin_lock(&mdsc->dentry_lru_lock);
+ list_add_tail(&di->lru, &mdsc->dentry_lru);
+ mdsc->num_dentry++;
+ spin_unlock(&mdsc->dentry_lru_lock);
+ }
+}
+
+void ceph_dentry_lru_touch(struct dentry *dn)
+{
+ struct ceph_dentry_info *di = ceph_dentry(dn);
+ struct ceph_mds_client *mdsc;
+
+ if (di) {
+ mdsc = &ceph_client(dn->d_sb)->mdsc;
+ spin_lock(&mdsc->dentry_lru_lock);
+ list_move_tail(&di->lru, &mdsc->dentry_lru);
+ spin_unlock(&mdsc->dentry_lru_lock);
+ }
+}
+
+void ceph_dentry_lru_del(struct dentry *dn)
+{
+ struct ceph_dentry_info *di = ceph_dentry(dn);
+ struct ceph_mds_client *mdsc;
+
+ if (di) {
+ mdsc = &ceph_client(dn->d_sb)->mdsc;
+ spin_lock(&mdsc->dentry_lru_lock);
+ list_del_init(&di->lru);
+ mdsc->num_dentry--;
+ spin_unlock(&mdsc->dentry_lru_lock);
+ }
+}
+
const struct file_operations ceph_dir_fops = {
.read = ceph_read_dir,
.readdir = ceph_readdir,
return err;
}
+static int __init_ceph_dentry(struct dentry *dentry, int locked)
+{
+ struct ceph_dentry_info *di;
+
+ if (locked)
+ spin_unlock(&dentry->d_lock);
+ di = kmalloc(sizeof(struct ceph_dentry_info),
+ GFP_NOFS);
+
+ if (locked)
+ spin_lock(&dentry->d_lock);
+ if (!di)
+ return -ENOMEM; /* oh well */
+
+ if (dentry->d_fsdata) {
+ kfree(di); /* lost a race! */
+ return 0;
+ }
+ dentry->d_fsdata = di;
+ di->dentry = dentry;
+ di->lease_session = NULL;
+ ceph_dentry_lru_add(dentry);
+
+ return 0;
+}
+
/*
* caller should hold session s_mutex.
*/
unsigned long from_time)
{
struct ceph_dentry_info *di;
- int is_new = 0;
long unsigned duration = le32_to_cpu(lease->duration_ms);
long unsigned ttl = from_time + (duration * HZ) / 1000;
long unsigned half_ttl = from_time + (duration * HZ / 2) / 1000;
goto out_unlock; /* we already have a newer lease. */
if (!di) {
- spin_unlock(&dentry->d_lock);
- di = kmalloc(sizeof(struct ceph_dentry_info),
- GFP_NOFS);
+ __init_ceph_dentry(dentry, 1);
+ di = ceph_dentry(dentry);
if (!di)
- return; /* oh well */
- spin_lock(&dentry->d_lock);
- if (dentry->d_fsdata) {
- kfree(di); /* lost a race! */
goto out_unlock;
- }
- dentry->d_fsdata = di;
- di->lease_session = ceph_get_mds_session(session);
- di->lease_gen = session->s_cap_gen;
- di->lease_seq = le32_to_cpu(lease->seq);
- is_new = 1;
- } else if (di->lease_session != session)
+ } else if (di->lease_session != session) {
goto out_unlock;
+ } else {
+ ceph_dentry_lru_touch(dentry);
+ }
+ di->lease_session = ceph_get_mds_session(session);
+ di->lease_gen = session->s_cap_gen;
+ di->lease_seq = le32_to_cpu(lease->seq);
di->lease_renew_after = half_ttl;
di->lease_renew_from = 0;
dentry->d_time = ttl;
dput(dn);
dn = realdn;
ceph_init_dentry(dn);
+ __init_ceph_dentry(dn, 0);
} else {
dout(10, "dn %p attached to %p ino %llx.%llx\n",
dn, dn->d_inode, ceph_vinop(dn->d_inode));
struct ceph_dentry_info *di = ceph_dentry(dentry);
ceph_put_mds_session(di->lease_session);
+ ceph_dentry_lru_del(dentry);
kfree(di);
dentry->d_fsdata = NULL;
}
spin_lock_init(&mdsc->cap_delay_lock);
INIT_LIST_HEAD(&mdsc->snap_flush_list);
spin_lock_init(&mdsc->snap_flush_lock);
+ spin_lock_init(&mdsc->dentry_lru_lock);
+ INIT_LIST_HEAD(&mdsc->dentry_lru);
}
/*
spinlock_t snap_flush_lock;
struct dentry *debugfs_file;
+
+ spinlock_t dentry_lru_lock;
+ struct list_head dentry_lru;
+ int num_dentry;
};
extern const char *ceph_mds_op_name(int op);
struct ceph_client_attr k_fsid, k_monmap, k_mdsmap, k_osdmap;
struct dentry *debugfs_fsid, *debugfs_monmap;
struct dentry *debugfs_mdsmap, *debugfs_osdmap;
- struct dentry *debugfs_dir;
+ struct dentry *debugfs_dir, *debugfs_dentry_lru;
struct mutex mount_mutex; /* serialize mount attempts */
struct ceph_mount_args mount_args;
u32 lease_gen;
u32 lease_seq;
unsigned long lease_renew_after, lease_renew_from;
+ struct list_head lru;
+ struct dentry *dentry;
};
static inline struct ceph_dentry_info *ceph_dentry(struct dentry *dentry)
extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
struct dentry *dentry, int err);
+extern void ceph_dentry_lru_add(struct dentry *dn);
+extern void ceph_dentry_lru_touch(struct dentry *dn);
+extern void ceph_dentry_lru_del(struct dentry *dn);
+
/*
* our d_ops vary depending on whether the inode is live,
* snapshotted (read-only), or a virtual ".snap" directory.