diff options
| -rw-r--r-- | fs/ceph/dir.c | 6 | ||||
| -rw-r--r-- | fs/ceph/inode.c | 16 | ||||
| -rw-r--r-- | fs/ceph/mds_client.c | 70 | ||||
| -rw-r--r-- | fs/ceph/snap.c | 7 |
4 files changed, 85 insertions, 14 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index a8f429882249..0637149fb9f9 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
| @@ -1766,6 +1766,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, | |||
| 1766 | unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) | 1766 | unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) |
| 1767 | { | 1767 | { |
| 1768 | struct ceph_inode_info *dci = ceph_inode(dir); | 1768 | struct ceph_inode_info *dci = ceph_inode(dir); |
| 1769 | unsigned hash; | ||
| 1769 | 1770 | ||
| 1770 | switch (dci->i_dir_layout.dl_dir_hash) { | 1771 | switch (dci->i_dir_layout.dl_dir_hash) { |
| 1771 | case 0: /* for backward compat */ | 1772 | case 0: /* for backward compat */ |
| @@ -1773,8 +1774,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) | |||
| 1773 | return dn->d_name.hash; | 1774 | return dn->d_name.hash; |
| 1774 | 1775 | ||
| 1775 | default: | 1776 | default: |
| 1776 | return ceph_str_hash(dci->i_dir_layout.dl_dir_hash, | 1777 | spin_lock(&dn->d_lock); |
| 1778 | hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash, | ||
| 1777 | dn->d_name.name, dn->d_name.len); | 1779 | dn->d_name.name, dn->d_name.len); |
| 1780 | spin_unlock(&dn->d_lock); | ||
| 1781 | return hash; | ||
| 1778 | } | 1782 | } |
| 1779 | } | 1783 | } |
| 1780 | 1784 | ||
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2d61ddda9bf5..c2feb310ac1e 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
| @@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) | |||
| 1163 | return 0; | 1163 | return 0; |
| 1164 | } | 1164 | } |
| 1165 | 1165 | ||
| 1166 | static int d_name_cmp(struct dentry *dentry, const char *name, size_t len) | ||
| 1167 | { | ||
| 1168 | int ret; | ||
| 1169 | |||
| 1170 | /* take d_lock to ensure dentry->d_name stability */ | ||
| 1171 | spin_lock(&dentry->d_lock); | ||
| 1172 | ret = dentry->d_name.len - len; | ||
| 1173 | if (!ret) | ||
| 1174 | ret = memcmp(dentry->d_name.name, name, len); | ||
| 1175 | spin_unlock(&dentry->d_lock); | ||
| 1176 | return ret; | ||
| 1177 | } | ||
| 1178 | |||
| 1166 | /* | 1179 | /* |
| 1167 | * Incorporate results into the local cache. This is either just | 1180 | * Incorporate results into the local cache. This is either just |
| 1168 | * one inode, or a directory, dentry, and possibly linked-to inode (e.g., | 1181 | * one inode, or a directory, dentry, and possibly linked-to inode (e.g., |
| @@ -1412,7 +1425,8 @@ retry_lookup: | |||
| 1412 | err = splice_dentry(&req->r_dentry, in); | 1425 | err = splice_dentry(&req->r_dentry, in); |
| 1413 | if (err < 0) | 1426 | if (err < 0) |
| 1414 | goto done; | 1427 | goto done; |
| 1415 | } else if (rinfo->head->is_dentry) { | 1428 | } else if (rinfo->head->is_dentry && |
| 1429 | !d_name_cmp(req->r_dentry, rinfo->dname, rinfo->dname_len)) { | ||
| 1416 | struct ceph_vino *ptvino = NULL; | 1430 | struct ceph_vino *ptvino = NULL; |
| 1417 | 1431 | ||
| 1418 | if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || | 1432 | if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || |
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 21c33ed048ed..9049c2a3e972 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c | |||
| @@ -1414,6 +1414,15 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, | |||
| 1414 | list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove); | 1414 | list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove); |
| 1415 | ci->i_prealloc_cap_flush = NULL; | 1415 | ci->i_prealloc_cap_flush = NULL; |
| 1416 | } | 1416 | } |
| 1417 | |||
| 1418 | if (drop && | ||
| 1419 | ci->i_wrbuffer_ref_head == 0 && | ||
| 1420 | ci->i_wr_ref == 0 && | ||
| 1421 | ci->i_dirty_caps == 0 && | ||
| 1422 | ci->i_flushing_caps == 0) { | ||
| 1423 | ceph_put_snap_context(ci->i_head_snapc); | ||
| 1424 | ci->i_head_snapc = NULL; | ||
| 1425 | } | ||
| 1417 | } | 1426 | } |
| 1418 | spin_unlock(&ci->i_ceph_lock); | 1427 | spin_unlock(&ci->i_ceph_lock); |
| 1419 | while (!list_empty(&to_remove)) { | 1428 | while (!list_empty(&to_remove)) { |
| @@ -2161,10 +2170,39 @@ retry: | |||
| 2161 | return path; | 2170 | return path; |
| 2162 | } | 2171 | } |
| 2163 | 2172 | ||
| 2173 | /* Duplicate the dentry->d_name.name safely */ | ||
| 2174 | static int clone_dentry_name(struct dentry *dentry, const char **ppath, | ||
| 2175 | int *ppathlen) | ||
| 2176 | { | ||
| 2177 | u32 len; | ||
| 2178 | char *name; | ||
| 2179 | |||
| 2180 | retry: | ||
| 2181 | len = READ_ONCE(dentry->d_name.len); | ||
| 2182 | name = kmalloc(len + 1, GFP_NOFS); | ||
| 2183 | if (!name) | ||
| 2184 | return -ENOMEM; | ||
| 2185 | |||
| 2186 | spin_lock(&dentry->d_lock); | ||
| 2187 | if (dentry->d_name.len != len) { | ||
| 2188 | spin_unlock(&dentry->d_lock); | ||
| 2189 | kfree(name); | ||
| 2190 | goto retry; | ||
| 2191 | } | ||
| 2192 | memcpy(name, dentry->d_name.name, len); | ||
| 2193 | spin_unlock(&dentry->d_lock); | ||
| 2194 | |||
| 2195 | name[len] = '\0'; | ||
| 2196 | *ppath = name; | ||
| 2197 | *ppathlen = len; | ||
| 2198 | return 0; | ||
| 2199 | } | ||
| 2200 | |||
| 2164 | static int build_dentry_path(struct dentry *dentry, struct inode *dir, | 2201 | static int build_dentry_path(struct dentry *dentry, struct inode *dir, |
| 2165 | const char **ppath, int *ppathlen, u64 *pino, | 2202 | const char **ppath, int *ppathlen, u64 *pino, |
| 2166 | int *pfreepath) | 2203 | bool *pfreepath, bool parent_locked) |
| 2167 | { | 2204 | { |
| 2205 | int ret; | ||
| 2168 | char *path; | 2206 | char *path; |
| 2169 | 2207 | ||
| 2170 | rcu_read_lock(); | 2208 | rcu_read_lock(); |
| @@ -2173,8 +2211,15 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, | |||
| 2173 | if (dir && ceph_snap(dir) == CEPH_NOSNAP) { | 2211 | if (dir && ceph_snap(dir) == CEPH_NOSNAP) { |
| 2174 | *pino = ceph_ino(dir); | 2212 | *pino = ceph_ino(dir); |
| 2175 | rcu_read_unlock(); | 2213 | rcu_read_unlock(); |
| 2176 | *ppath = dentry->d_name.name; | 2214 | if (parent_locked) { |
| 2177 | *ppathlen = dentry->d_name.len; | 2215 | *ppath = dentry->d_name.name; |
| 2216 | *ppathlen = dentry->d_name.len; | ||
| 2217 | } else { | ||
| 2218 | ret = clone_dentry_name(dentry, ppath, ppathlen); | ||
| 2219 | if (ret) | ||
| 2220 | return ret; | ||
| 2221 | *pfreepath = true; | ||
| 2222 | } | ||
| 2178 | return 0; | 2223 | return 0; |
| 2179 | } | 2224 | } |
| 2180 | rcu_read_unlock(); | 2225 | rcu_read_unlock(); |
| @@ -2182,13 +2227,13 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, | |||
| 2182 | if (IS_ERR(path)) | 2227 | if (IS_ERR(path)) |
| 2183 | return PTR_ERR(path); | 2228 | return PTR_ERR(path); |
| 2184 | *ppath = path; | 2229 | *ppath = path; |
| 2185 | *pfreepath = 1; | 2230 | *pfreepath = true; |
| 2186 | return 0; | 2231 | return 0; |
| 2187 | } | 2232 | } |
| 2188 | 2233 | ||
| 2189 | static int build_inode_path(struct inode *inode, | 2234 | static int build_inode_path(struct inode *inode, |
| 2190 | const char **ppath, int *ppathlen, u64 *pino, | 2235 | const char **ppath, int *ppathlen, u64 *pino, |
| 2191 | int *pfreepath) | 2236 | bool *pfreepath) |
| 2192 | { | 2237 | { |
| 2193 | struct dentry *dentry; | 2238 | struct dentry *dentry; |
| 2194 | char *path; | 2239 | char *path; |
| @@ -2204,7 +2249,7 @@ static int build_inode_path(struct inode *inode, | |||
| 2204 | if (IS_ERR(path)) | 2249 | if (IS_ERR(path)) |
| 2205 | return PTR_ERR(path); | 2250 | return PTR_ERR(path); |
| 2206 | *ppath = path; | 2251 | *ppath = path; |
| 2207 | *pfreepath = 1; | 2252 | *pfreepath = true; |
| 2208 | return 0; | 2253 | return 0; |
| 2209 | } | 2254 | } |
| 2210 | 2255 | ||
| @@ -2215,7 +2260,7 @@ static int build_inode_path(struct inode *inode, | |||
| 2215 | static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, | 2260 | static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, |
| 2216 | struct inode *rdiri, const char *rpath, | 2261 | struct inode *rdiri, const char *rpath, |
| 2217 | u64 rino, const char **ppath, int *pathlen, | 2262 | u64 rino, const char **ppath, int *pathlen, |
| 2218 | u64 *ino, int *freepath) | 2263 | u64 *ino, bool *freepath, bool parent_locked) |
| 2219 | { | 2264 | { |
| 2220 | int r = 0; | 2265 | int r = 0; |
| 2221 | 2266 | ||
| @@ -2225,7 +2270,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, | |||
| 2225 | ceph_snap(rinode)); | 2270 | ceph_snap(rinode)); |
| 2226 | } else if (rdentry) { | 2271 | } else if (rdentry) { |
| 2227 | r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino, | 2272 | r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino, |
| 2228 | freepath); | 2273 | freepath, parent_locked); |
| 2229 | dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, | 2274 | dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, |
| 2230 | *ppath); | 2275 | *ppath); |
| 2231 | } else if (rpath || rino) { | 2276 | } else if (rpath || rino) { |
| @@ -2251,7 +2296,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, | |||
| 2251 | const char *path2 = NULL; | 2296 | const char *path2 = NULL; |
| 2252 | u64 ino1 = 0, ino2 = 0; | 2297 | u64 ino1 = 0, ino2 = 0; |
| 2253 | int pathlen1 = 0, pathlen2 = 0; | 2298 | int pathlen1 = 0, pathlen2 = 0; |
| 2254 | int freepath1 = 0, freepath2 = 0; | 2299 | bool freepath1 = false, freepath2 = false; |
| 2255 | int len; | 2300 | int len; |
| 2256 | u16 releases; | 2301 | u16 releases; |
| 2257 | void *p, *end; | 2302 | void *p, *end; |
| @@ -2259,16 +2304,19 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, | |||
| 2259 | 2304 | ||
| 2260 | ret = set_request_path_attr(req->r_inode, req->r_dentry, | 2305 | ret = set_request_path_attr(req->r_inode, req->r_dentry, |
| 2261 | req->r_parent, req->r_path1, req->r_ino1.ino, | 2306 | req->r_parent, req->r_path1, req->r_ino1.ino, |
| 2262 | &path1, &pathlen1, &ino1, &freepath1); | 2307 | &path1, &pathlen1, &ino1, &freepath1, |
| 2308 | test_bit(CEPH_MDS_R_PARENT_LOCKED, | ||
| 2309 | &req->r_req_flags)); | ||
| 2263 | if (ret < 0) { | 2310 | if (ret < 0) { |
| 2264 | msg = ERR_PTR(ret); | 2311 | msg = ERR_PTR(ret); |
| 2265 | goto out; | 2312 | goto out; |
| 2266 | } | 2313 | } |
| 2267 | 2314 | ||
| 2315 | /* If r_old_dentry is set, then assume that its parent is locked */ | ||
| 2268 | ret = set_request_path_attr(NULL, req->r_old_dentry, | 2316 | ret = set_request_path_attr(NULL, req->r_old_dentry, |
| 2269 | req->r_old_dentry_dir, | 2317 | req->r_old_dentry_dir, |
| 2270 | req->r_path2, req->r_ino2.ino, | 2318 | req->r_path2, req->r_ino2.ino, |
| 2271 | &path2, &pathlen2, &ino2, &freepath2); | 2319 | &path2, &pathlen2, &ino2, &freepath2, true); |
| 2272 | if (ret < 0) { | 2320 | if (ret < 0) { |
| 2273 | msg = ERR_PTR(ret); | 2321 | msg = ERR_PTR(ret); |
| 2274 | goto out_free1; | 2322 | goto out_free1; |
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 89aa37fa0f84..b26e12cd8ec3 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c | |||
| @@ -572,7 +572,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) | |||
| 572 | old_snapc = NULL; | 572 | old_snapc = NULL; |
| 573 | 573 | ||
| 574 | update_snapc: | 574 | update_snapc: |
| 575 | if (ci->i_head_snapc) { | 575 | if (ci->i_wrbuffer_ref_head == 0 && |
| 576 | ci->i_wr_ref == 0 && | ||
| 577 | ci->i_dirty_caps == 0 && | ||
| 578 | ci->i_flushing_caps == 0) { | ||
| 579 | ci->i_head_snapc = NULL; | ||
| 580 | } else { | ||
| 576 | ci->i_head_snapc = ceph_get_snap_context(new_snapc); | 581 | ci->i_head_snapc = ceph_get_snap_context(new_snapc); |
| 577 | dout(" new snapc is %p\n", new_snapc); | 582 | dout(" new snapc is %p\n", new_snapc); |
| 578 | } | 583 | } |
