/* be careful with mtime, atime, size */
ceph_decode_timespec(&atime, &info->atime);
ceph_decode_timespec(&mtime, &info->mtime);
- issued = ceph_caps_issued(ci);
+ issued = __ceph_caps_issued(ci);
spin_lock(&inode->i_lock);
if (issued & (CEPH_CAP_WR|CEPH_CAP_WRBUFFER)) {
if ((issued & CEPH_CAP_EXCL) == 0) {
void ceph_revoke_inode_lease(struct ceph_inode_info *ci, int mask)
{
int drop = 0;
+
spin_lock(&ci->vfs_inode.i_lock);
+ dout(10, "revoke_inode_lease on inode %p, mask %d -> %d\n",
+ &ci->vfs_inode, ci->i_lease_mask, ci->i_lease_mask & ~mask);
ci->i_lease_mask &= ~mask;
if (ci->i_lease_mask == 0) {
spin_lock(&ci->i_lease_session->s_cap_lock);
}
}
+
/*
* dentry lease lock order is
* dentry->d_lock
* capabilities
*/
-struct ceph_inode_cap *ceph_find_cap(struct inode *inode, int want)
-{
- struct ceph_inode_info *ci = ceph_inode(inode);
- struct ceph_inode_cap *cap;
- struct list_head *p;
-
- list_for_each(p, &ci->i_caps) {
- cap = list_entry(p, struct ceph_inode_cap, ci_caps);
- if ((cap->caps & want) == want) {
- dout(40, "find_cap found %p caps %d want %d\n", cap,
- cap->caps, want);
- return cap;
- }
- }
- return 0;
-}
-
-static struct ceph_inode_cap *get_cap_for_mds(struct inode *inode, int mds)
+static struct ceph_inode_cap *__get_cap_for_mds(struct inode *inode, int mds)
{
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_inode_cap *cap;
return 0;
}
-
-struct ceph_inode_cap *ceph_add_cap(struct inode *inode, struct ceph_mds_session *session, u32 caps, u32 seq)
+/*
+ * lock ordering is
+ * inode->i_lock
+ * session->s_cap_lock
+ */
+struct ceph_inode_cap *ceph_add_cap(struct inode *inode,
+ struct ceph_mds_session *session,
+ u32 caps, u32 seq)
{
int mds = session->s_mds;
struct ceph_inode_info *ci = ceph_inode(inode);
- struct ceph_inode_cap *cap;
+ struct ceph_inode_cap *cap, *new_cap = 0;
int i;
+ int is_new = 0;
- dout(10, "ceph_add_cap on %p mds%d cap %d seq %d\n", inode, session->s_mds, caps, seq);
- cap = get_cap_for_mds(inode, mds);
+ dout(10, "ceph_add_cap on %p mds%d cap %d seq %d\n", inode,
+ session->s_mds, caps, seq);
+ spin_lock(&inode->i_lock);
+ cap = __get_cap_for_mds(inode, mds);
if (!cap) {
for (i=0; i<STATIC_CAPS; i++)
if (ci->i_static_caps[i].mds == -1) {
cap = &ci->i_static_caps[i];
break;
}
- if (!cap)
- cap = kmalloc(sizeof(*cap), GFP_KERNEL);
- if (cap == 0)
- return ERR_PTR(-ENOMEM);
+ if (!cap) {
+ if (new_cap)
+ cap = new_cap;
+ else {
+ spin_unlock(&inode->i_lock);
+ new_cap = kmalloc(sizeof(*cap), GFP_KERNEL);
+ if (new_cap == 0)
+ return ERR_PTR(-ENOMEM);
+ spin_lock(&inode->i_lock);
+ }
+ }
+ is_new = 1; /* grab inode later */
cap->caps = 0;
cap->mds = mds;
cap->seq = 0;
cap->flags = 0;
- if (list_empty(&ci->i_caps))
- igrab(inode);
cap->ci = ci;
list_add(&cap->ci_caps, &ci->i_caps);
+ /* add to session cap list */
cap->session = session;
spin_lock(&session->s_cap_lock);
list_add(&cap->session_caps, &session->s_caps);
inode, ceph_ino(inode), caps, caps|cap->caps, seq, mds);
cap->caps |= caps;
cap->seq = seq;
+ spin_unlock(&inode->i_lock);
+ if (is_new)
+ igrab(inode);
return cap;
}
-int ceph_caps_issued(struct ceph_inode_info *ci)
+int __ceph_caps_issued(struct ceph_inode_info *ci)
{
int have = 0;
struct ceph_inode_cap *cap;
return have;
}
-void ceph_remove_cap(struct ceph_inode_cap *cap)
+void __ceph_remove_cap(struct ceph_inode_cap *cap)
{
struct ceph_mds_session *session = cap->session;
- struct ceph_inode_info *ci = cap->ci;
- dout(10, "ceph_remove_cap %p from %p\n", cap, &ci->vfs_inode);
+ dout(10, "__ceph_remove_cap %p from %p\n", cap, &cap->ci->vfs_inode);
/* remove from session list */
spin_lock(&session->s_cap_lock);
list_del(&cap->session_caps);
session->s_nr_caps--;
- cap->session = 0;
spin_unlock(&session->s_cap_lock);
/* remove from inode list */
- spin_lock(&ci->vfs_inode.i_lock);
list_del(&cap->ci_caps);
- if (list_empty(&ci->i_caps))
- iput(&ci->vfs_inode);
- cap->ci = 0;
+ cap->session = 0;
cap->mds = -1; /* mark unused */
- spin_unlock(&ci->vfs_inode.i_lock);
- if (cap < ci->i_static_caps || cap >= ci->i_static_caps + STATIC_CAPS)
+ if (cap < cap->ci->i_static_caps ||
+ cap >= cap->ci->i_static_caps + STATIC_CAPS)
kfree(cap);
}
-void ceph_remove_all_caps(struct ceph_inode_info *ci)
+void ceph_remove_cap(struct ceph_inode_cap *cap)
{
+ struct inode *inode = &cap->ci->vfs_inode;
+
+ spin_lock(&inode->i_lock);
+ __ceph_remove_cap(cap);
+ spin_unlock(&inode->i_lock);
+ iput(inode);
+}
+
+/*
+ * examine currently wanted versus held caps, and release caps to mds
+ * as appropriate.
+ */
+void ceph_check_caps_wanted(struct ceph_inode_info *ci)
+{
+ struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode);
+ struct ceph_mds_client *mdsc = &client->mdsc;
struct ceph_inode_cap *cap;
- dout(10, "remove_caps on %p\n", &ci->vfs_inode);
- while (!list_empty(&ci->i_caps)) {
- cap = list_entry(ci->i_caps.next, struct ceph_inode_cap,
- ci_caps);
- ceph_remove_cap(cap);
+ struct list_head *p;
+ int wanted;
+ int keep;
+ __u64 seq;
+ __u64 size, max_size;
+ struct timespec mtime, atime;
+ int mds;
+
+retry:
+ spin_lock(&ci->vfs_inode.i_lock);
+ wanted = __ceph_caps_wanted(ci);
+
+ list_for_each(p, &ci->i_caps) {
+ cap = list_entry(p, struct ceph_inode_cap, ci_caps);
+ if ((cap->caps & ~wanted) == 0)
+ continue; /* nothing extra, all good */
+
+ cap->caps &= wanted; /* drop bits we don't want */
+
+ keep = cap->caps;
+ seq = cap->seq;
+ size = ci->vfs_inode.i_size;
+ max_size = ci->i_max_size;
+ mtime = ci->vfs_inode.i_mtime;
+ atime = ci->vfs_inode.i_atime;
+ mds = cap->mds;
+ if (wanted == 0)
+ __ceph_remove_cap(cap);
+ spin_unlock(&ci->vfs_inode.i_lock);
+
+ ceph_mdsc_send_cap_ack(mdsc, ceph_ino(&ci->vfs_inode),
+ keep, wanted, seq,
+ size, max_size, &mtime, &atime, mds);
+
+ if (wanted == 0)
+ iput(&ci->vfs_inode); /* removed cap */
+ goto retry;
}
+
+ /* okay */
+ spin_unlock(&ci->vfs_inode.i_lock);
}
+/*
+ * called on struct file init and release, to safely track which
+ * capabilities we want due to currently open files
+ */
+void ceph_get_mode(struct ceph_inode_info *ci, int mode)
+{
+ spin_lock(&ci->vfs_inode.i_lock);
+ ci->i_nr_by_mode[mode]++;
+ spin_unlock(&ci->vfs_inode.i_lock);
+}
+
+void ceph_put_mode(struct ceph_inode_info *ci, int mode)
+{
+ int last = 0;
+
+ spin_lock(&ci->vfs_inode.i_lock);
+ if (--ci->i_nr_by_mode[mode] == 0)
+ last++;
+ spin_unlock(&ci->vfs_inode.i_lock);
+
+ if (last)
+ ceph_check_caps_wanted(ci);
+}
+
+
/*
* 0 - ok
* 1 - send the msg back to mds
*/
-int ceph_handle_cap_grant(struct inode *inode, struct ceph_mds_file_caps *grant, struct ceph_mds_session *session)
+int ceph_handle_cap_grant(struct inode *inode, struct ceph_mds_file_caps *grant,
+ struct ceph_mds_session *session)
{
struct ceph_inode_cap *cap;
struct ceph_inode_info *ci = ceph_inode(inode);
int newcaps;
int used;
int issued; /* to me, before */
- int wanted = ceph_caps_wanted(ci) | ceph_caps_used(ci);
+ int wanted;
int ret = 0;
u64 size = le64_to_cpu(grant->size);
u64 max_size = le64_to_cpu(grant->max_size);
dout(10, "handle_cap_grant inode %p ci %p mds%d seq %d\n",
inode, ci, mds, seq);
- dout(10, " my wanted = %d\n", wanted);
dout(10, " size %llu max_size %llu\n", size, max_size);
/* size change? */
}
/* mtime/atime? */
- issued = ceph_caps_issued(ci);
+ issued = __ceph_caps_issued(ci);
if ((issued & CEPH_CAP_EXCL) == 0) {
ceph_decode_timespec(&mtime, &grant->mtime);
ceph_decode_timespec(&atime, &grant->atime);
}
}
- cap = get_cap_for_mds(inode, mds);
-
/* do we have this cap? */
+ cap = __get_cap_for_mds(inode, mds);
if (!cap) {
/*
* then ignore. never reply to cap messages out of turn,
goto out;
}
- cap->seq = seq;
-
+ wanted = __ceph_caps_wanted(ci);
+ used = __ceph_caps_used(ci);
+ dout(10, " my wanted = %d, used = %d\n", wanted, used);
if (wanted != le32_to_cpu(grant->wanted)) {
- dout(10, "wanted %d -> %d\n", le32_to_cpu(grant->wanted),
+ dout(10, "mds wanted %d -> %d\n", le32_to_cpu(grant->wanted),
wanted);
grant->wanted = cpu_to_le32(wanted);
}
+ cap->seq = seq;
+
/* revocation? */
newcaps = le32_to_cpu(grant->caps);
if (cap->caps & ~newcaps) {
- used = ceph_caps_used(ci);
- dout(10, "revocation: %d -> %d, used %d\n", cap->caps,
- newcaps, used);
+ dout(10, "revocation: %d -> %d\n", cap->caps, newcaps);
if (newcaps & used) {
/* FIXME FIXME FIXME DO STUFF HERE */
/* but blindly ack for now... */
dout(10, "get_cap_refs on %p need %d want %d\n", &ci->vfs_inode,
need, want);
spin_lock(&ci->vfs_inode.i_lock);
- have = ceph_caps_issued(ci);
+ have = __ceph_caps_issued(ci);
if ((have & need) == need) {
*got = need | (have & want);
__take_cap_refs(ci, *got);
void ceph_put_cap_refs(struct ceph_inode_info *ci, int had)
{
- int last = 0, wanted;
+ int last = 0;
spin_lock(&ci->vfs_inode.i_lock);
if (had & CEPH_CAP_RD)
last++;
spin_unlock(&ci->vfs_inode.i_lock);
- if (last) {
- wanted = ceph_caps_wanted(ci);
- wanted |= ceph_caps_used(ci);
- if (wanted != ci->i_cap_wanted)
- ceph_mdsc_update_cap_wanted(ci, wanted);
- }
+ if (last)
+ ceph_check_caps_wanted(ci);
}
((ia_valid & ATTR_MTIME) &&
!timespec_equal(&inode->i_mtime, &attr->ia_mtime))) {
/* do i hold CAP_EXCL? */
- if (ceph_caps_issued(ci) & CEPH_CAP_EXCL) {
+ if (__ceph_caps_issued(ci) & CEPH_CAP_EXCL) {
dout(10, "utime holding EXCL, doing nothing\n");
inode->i_atime = attr->ia_atime;
inode->i_mtime = attr->ia_mtime;
int ceph_inode_revalidate(struct inode *inode, int mask)
{
struct ceph_inode_info *ci = ceph_inode(inode);
- int havemask = ci->i_lease_mask;
+ int havemask;
+ int valid;
+ spin_lock(&inode->i_lock);
+ havemask = ci->i_lease_mask;
/* EXCL cap counts for an ICONTENT lease */
- if (ceph_caps_issued(ci) & CEPH_CAP_EXCL)
+ if (__ceph_caps_issued(ci) & CEPH_CAP_EXCL)
havemask |= CEPH_LOCK_ICONTENT;
/* any ICONTENT bits imply all bits */
if (havemask & CEPH_LOCK_ICONTENT)
havemask |= CEPH_LOCK_ICONTENT;
-
- if (time_before(jiffies, ci->i_lease_ttl)) {
+ valid = time_before(jiffies, ci->i_lease_ttl);
+ spin_unlock(&inode->i_lock);
+
+ if (valid) {
if ((havemask & mask) == mask) {
dout(10, "inode_revalidate %p mask %d still valid\n",
inode, mask);
} else {
dout(10, "inode_revalidate %p have %d want %d, lease expired\n",
inode, havemask, mask);
- ceph_revoke_inode_lease(ci, mask);
}
+
return ceph_do_lookup(inode->i_sb, d_find_alias(inode), mask);
}
cap = list_entry(session->s_caps.next, struct ceph_inode_cap,
session_caps);
ci = cap->ci;
- igrab(&ci->vfs_inode);
dout(10, "removing cap %p, ci is %p, inode is %p\n",
cap, ci, &ci->vfs_inode);
spin_unlock(&session->s_cap_lock);
ceph_remove_cap(cap);
spin_lock(&session->s_cap_lock);
- iput(&ci->vfs_inode);
}
BUG_ON(session->s_nr_caps > 0);
spin_unlock(&session->s_cap_lock);
rec = p;
p += sizeof(*rec);
BUG_ON(p > end);
- rec->wanted = cpu_to_le32(ceph_caps_wanted(ci));
- rec->issued = cpu_to_le32(ceph_caps_issued(ci));
+ spin_lock(&ci->vfs_inode.i_lock);
+ rec->wanted = cpu_to_le32(__ceph_caps_wanted(ci));
+ rec->issued = cpu_to_le32(__ceph_caps_issued(ci));
rec->size = cpu_to_le64(ci->vfs_inode.i_size);
ceph_encode_timespec(&rec->mtime, &ci->vfs_inode.i_mtime);
ceph_encode_timespec(&rec->atime, &ci->vfs_inode.i_atime);
+ spin_unlock(&ci->vfs_inode.i_lock);
dentry = d_find_alias(&ci->vfs_inode);
path = ceph_build_dentry_path(dentry, &pathlen);
if (IS_ERR(path)) {
/* caps */
-void send_cap_ack(struct ceph_mds_client *mdsc, __u64 ino, int caps,
- int wanted, __u32 seq, __u64 size, __u64 max_size,
- struct timespec *mtime, struct timespec *atime,
- int mds)
+void ceph_mdsc_send_cap_ack(struct ceph_mds_client *mdsc, __u64 ino, int caps,
+ int wanted, __u32 seq, __u64 size, __u64 max_size,
+ struct timespec *mtime, struct timespec *atime,
+ int mds)
{
struct ceph_mds_file_caps *fc;
struct ceph_msg *msg;
if (!inode) {
dout(10, "wtf, i don't have ino %lu=%llx? closing out cap\n",
inot, ino);
- send_cap_ack(mdsc, ino, 0, 0, seq, size, max_size, 0, 0, mds);
+ ceph_mdsc_send_cap_ack(mdsc, ino, 0, 0, seq,
+ size, max_size, 0, 0, mds);
return;
}
return;
}
-int ceph_mdsc_update_cap_wanted(struct ceph_inode_info *ci, int wanted)
-{
- struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode);
- struct ceph_mds_client *mdsc = &client->mdsc;
- struct ceph_inode_cap *cap;
- struct ceph_mds_session *session;
- struct list_head *p;
-
- dout(10, "update_cap_wanted %d -> %d\n", ci->i_cap_wanted, wanted);
-
- list_for_each(p, &ci->i_caps) {
- cap = list_entry(p, struct ceph_inode_cap, ci_caps);
-
- session = __get_session(mdsc, cap->mds);
- BUG_ON(!session);
-
- cap->caps &= wanted; /* drop caps we don't want */
- send_cap_ack(mdsc, ceph_ino(&ci->vfs_inode), cap->caps, wanted,
- cap->seq, ci->vfs_inode.i_size, ci->i_max_size,
- &ci->vfs_inode.i_mtime, &ci->vfs_inode.i_atime,
- cap->mds);
- }
-
- ci->i_cap_wanted = wanted;
- if (wanted == 0)
- ceph_remove_all_caps(ci);
-
- return 0;
-}
-
int send_renewcaps(struct ceph_mds_client *mdsc, int mds)
{
struct ceph_msg *msg;
/* inode */
ci = ceph_inode(inode);
- if (mask & ci->i_lease_mask) {
- ceph_revoke_inode_lease(ci, mask);
- dout(10, "lease mask %d revoked on inode %p, still have %d\n",
- mask, inode, ci->i_lease_mask);
- }
+ ceph_revoke_inode_lease(ci, mask);
/* dentry */
if (mask & CEPH_LOCK_DN) {