]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ceph/mds_client.c
ceph: fix wake_up_session_cb()
[karo-tx-linux.git] / fs / ceph / mds_client.c
index cff85af425d4ea7bd94715f79d85c4b9101d5ede..2103b823bec0786aa60e48fa3957f252d5010017 100644 (file)
@@ -181,17 +181,18 @@ static int parse_reply_info_dir(void **p, void *end,
 
        ceph_decode_need(p, end, sizeof(num) + 2, bad);
        num = ceph_decode_32(p);
-       info->dir_end = ceph_decode_8(p);
-       info->dir_complete = ceph_decode_8(p);
+       {
+               u16 flags = ceph_decode_16(p);
+               info->dir_end = !!(flags & CEPH_READDIR_FRAG_END);
+               info->dir_complete = !!(flags & CEPH_READDIR_FRAG_COMPLETE);
+               info->hash_order = !!(flags & CEPH_READDIR_HASH_ORDER);
+       }
        if (num == 0)
                goto done;
 
-       BUG_ON(!info->dir_in);
-       info->dir_dname = (void *)(info->dir_in + num);
-       info->dir_dname_len = (void *)(info->dir_dname + num);
-       info->dir_dlease = (void *)(info->dir_dname_len + num);
-       if ((unsigned long)(info->dir_dlease + num) >
-           (unsigned long)info->dir_in + info->dir_buf_size) {
+       BUG_ON(!info->dir_entries);
+       if ((unsigned long)(info->dir_entries + num) >
+           (unsigned long)info->dir_entries + info->dir_buf_size) {
                pr_err("dir contents are larger than expected\n");
                WARN_ON(1);
                goto bad;
@@ -199,21 +200,23 @@ static int parse_reply_info_dir(void **p, void *end,
 
        info->dir_nr = num;
        while (num) {
+               struct ceph_mds_reply_dir_entry *rde = info->dir_entries + i;
                /* dentry */
                ceph_decode_need(p, end, sizeof(u32)*2, bad);
-               info->dir_dname_len[i] = ceph_decode_32(p);
-               ceph_decode_need(p, end, info->dir_dname_len[i], bad);
-               info->dir_dname[i] = *p;
-               *p += info->dir_dname_len[i];
-               dout("parsed dir dname '%.*s'\n", info->dir_dname_len[i],
-                    info->dir_dname[i]);
-               info->dir_dlease[i] = *p;
+               rde->name_len = ceph_decode_32(p);
+               ceph_decode_need(p, end, rde->name_len, bad);
+               rde->name = *p;
+               *p += rde->name_len;
+               dout("parsed dir dname '%.*s'\n", rde->name_len, rde->name);
+               rde->lease = *p;
                *p += sizeof(struct ceph_mds_reply_lease);
 
                /* inode */
-               err = parse_reply_info_in(p, end, &info->dir_in[i], features);
+               err = parse_reply_info_in(p, end, &rde->inode, features);
                if (err < 0)
                        goto out_bad;
+               /* ceph_readdir_prepopulate() will update it */
+               rde->offset = 0;
                i++;
                num--;
        }
@@ -345,9 +348,9 @@ out_bad:
 
 static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
 {
-       if (!info->dir_in)
+       if (!info->dir_entries)
                return;
-       free_pages((unsigned long)info->dir_in, get_order(info->dir_buf_size));
+       free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
 }
 
 
@@ -839,12 +842,14 @@ static struct ceph_msg *create_session_open_msg(struct ceph_mds_client *mdsc, u6
        int metadata_bytes = 0;
        int metadata_key_count = 0;
        struct ceph_options *opt = mdsc->fsc->client->options;
+       struct ceph_mount_options *fsopt = mdsc->fsc->mount_options;
        void *p;
 
        const char* metadata[][2] = {
                {"hostname", utsname()->nodename},
                {"kernel_version", utsname()->release},
-               {"entity_id", opt->name ? opt->name : ""},
+               {"entity_id", opt->name ? : ""},
+               {"root", fsopt->server_path ? : "/"},
                {NULL, NULL}
        };
 
@@ -1120,9 +1125,11 @@ out:
 static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
                                  void *arg)
 {
+       struct ceph_fs_client *fsc = (struct ceph_fs_client *)arg;
        struct ceph_inode_info *ci = ceph_inode(inode);
        LIST_HEAD(to_remove);
-       int drop = 0;
+       bool drop = false;
+       bool invalidate = false;
 
        dout("removing cap %p, ci is %p, inode is %p\n",
             cap, ci, &ci->vfs_inode);
@@ -1130,8 +1137,13 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
        __ceph_remove_cap(cap, false);
        if (!ci->i_auth_cap) {
                struct ceph_cap_flush *cf;
-               struct ceph_mds_client *mdsc =
-                       ceph_sb_to_client(inode->i_sb)->mdsc;
+               struct ceph_mds_client *mdsc = fsc->mdsc;
+
+               ci->i_ceph_flags |= CEPH_I_CAP_DROPPED;
+
+               if (ci->i_wrbuffer_ref > 0 &&
+                   ACCESS_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN)
+                       invalidate = true;
 
                while (true) {
                        struct rb_node *n = rb_first(&ci->i_cap_flush_tree);
@@ -1154,7 +1166,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
                                inode, ceph_ino(inode));
                        ci->i_dirty_caps = 0;
                        list_del_init(&ci->i_dirty_item);
-                       drop = 1;
+                       drop = true;
                }
                if (!list_empty(&ci->i_flushing_item)) {
                        pr_warn_ratelimited(
@@ -1164,7 +1176,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
                        ci->i_flushing_caps = 0;
                        list_del_init(&ci->i_flushing_item);
                        mdsc->num_cap_flushing--;
-                       drop = 1;
+                       drop = true;
                }
                spin_unlock(&mdsc->cap_dirty_lock);
 
@@ -1181,7 +1193,11 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
                list_del(&cf->list);
                ceph_free_cap_flush(cf);
        }
-       while (drop--)
+
+       wake_up_all(&ci->i_cap_wq);
+       if (invalidate)
+               ceph_queue_invalidate(inode);
+       if (drop)
                iput(inode);
        return 0;
 }
@@ -1191,12 +1207,13 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
  */
 static void remove_session_caps(struct ceph_mds_session *session)
 {
+       struct ceph_fs_client *fsc = session->s_mdsc->fsc;
+       struct super_block *sb = fsc->sb;
        dout("remove_session_caps on %p\n", session);
-       iterate_session_caps(session, remove_session_caps_cb, NULL);
+       iterate_session_caps(session, remove_session_caps_cb, fsc);
 
        spin_lock(&session->s_cap_lock);
        if (session->s_nr_caps > 0) {
-               struct super_block *sb = session->s_mdsc->fsc->sb;
                struct inode *inode;
                struct ceph_cap *cap, *prev = NULL;
                struct ceph_vino vino;
@@ -1241,13 +1258,13 @@ static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap,
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
 
-       wake_up_all(&ci->i_cap_wq);
        if (arg) {
                spin_lock(&ci->i_ceph_lock);
                ci->i_wanted_max_size = 0;
                ci->i_requested_max_size = 0;
                spin_unlock(&ci->i_ceph_lock);
        }
+       wake_up_all(&ci->i_cap_wq);
        return 0;
 }
 
@@ -1642,8 +1659,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
        struct ceph_inode_info *ci = ceph_inode(dir);
        struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
        struct ceph_mount_options *opt = req->r_mdsc->fsc->mount_options;
-       size_t size = sizeof(*rinfo->dir_in) + sizeof(*rinfo->dir_dname_len) +
-                     sizeof(*rinfo->dir_dname) + sizeof(*rinfo->dir_dlease);
+       size_t size = sizeof(struct ceph_mds_reply_dir_entry);
        int order, num_entries;
 
        spin_lock(&ci->i_ceph_lock);
@@ -1654,14 +1670,14 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
 
        order = get_order(size * num_entries);
        while (order >= 0) {
-               rinfo->dir_in = (void*)__get_free_pages(GFP_KERNEL |
-                                                       __GFP_NOWARN,
-                                                       order);
-               if (rinfo->dir_in)
+               rinfo->dir_entries = (void*)__get_free_pages(GFP_KERNEL |
+                                                            __GFP_NOWARN,
+                                                            order);
+               if (rinfo->dir_entries)
                        break;
                order--;
        }
-       if (!rinfo->dir_in)
+       if (!rinfo->dir_entries)
                return -ENOMEM;
 
        num_entries = (PAGE_SIZE << order) / size;