Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ceph: avoid block operation when !TASK_RUNNING (ceph_get_caps)

we should not do block operation in wait_event_interruptible()'s condition
check function, but reading inline data can block. so move the read inline
data code to ceph_get_caps()

Signed-off-by: Yan, Zheng <zyan@redhat.com>

authored by

Yan, Zheng and committed by
Ilya Dryomov
c4d4a582 d3383a8e

+42 -44
+42 -44
fs/ceph/caps.c
··· 2072 2072 * requested from the MDS. 2073 2073 */ 2074 2074 static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, 2075 - loff_t endoff, int *got, struct page **pinned_page, 2076 - int *check_max, int *err) 2075 + loff_t endoff, int *got, int *check_max, int *err) 2077 2076 { 2078 2077 struct inode *inode = &ci->vfs_inode; 2079 2078 int ret = 0; 2080 - int have, implemented, _got = 0; 2079 + int have, implemented; 2081 2080 int file_wanted; 2082 2081 2083 2082 dout("get_cap_refs %p need %s want %s\n", inode, 2084 2083 ceph_cap_string(need), ceph_cap_string(want)); 2085 - again: 2084 + 2086 2085 spin_lock(&ci->i_ceph_lock); 2087 2086 2088 2087 /* make sure file is actually open */ ··· 2136 2137 inode, ceph_cap_string(have), ceph_cap_string(not), 2137 2138 ceph_cap_string(revoking)); 2138 2139 if ((revoking & not) == 0) { 2139 - _got = need | (have & want); 2140 - __take_cap_refs(ci, _got); 2140 + *got = need | (have & want); 2141 + __take_cap_refs(ci, *got); 2141 2142 ret = 1; 2142 2143 } 2143 2144 } else { ··· 2162 2163 out_unlock: 2163 2164 spin_unlock(&ci->i_ceph_lock); 2164 2165 2165 - if (ci->i_inline_version != CEPH_INLINE_NONE && 2166 - (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && 2167 - i_size_read(inode) > 0) { 2168 - int ret1; 2169 - struct page *page = find_get_page(inode->i_mapping, 0); 2170 - if (page) { 2171 - if (PageUptodate(page)) { 2172 - *pinned_page = page; 2173 - goto out; 2174 - } 2175 - page_cache_release(page); 2176 - } 2177 - /* 2178 - * drop cap refs first because getattr while holding 2179 - * caps refs can cause deadlock. 2180 - */ 2181 - ceph_put_cap_refs(ci, _got); 2182 - _got = 0; 2183 - 2184 - /* getattr request will bring inline data into page cache */ 2185 - ret1 = __ceph_do_getattr(inode, NULL, 2186 - CEPH_STAT_CAP_INLINE_DATA, true); 2187 - if (ret1 >= 0) { 2188 - ret = 0; 2189 - goto again; 2190 - } 2191 - *err = ret1; 2192 - ret = 1; 2193 - } 2194 - out: 2195 2166 dout("get_cap_refs %p ret %d got %s\n", inode, 2196 - ret, ceph_cap_string(_got)); 2197 - *got = _got; 2167 + ret, ceph_cap_string(*got)); 2198 2168 return ret; 2199 2169 } 2200 2170 ··· 2203 2235 int ceph_get_caps(struct ceph_inode_info *ci, int need, int want, 2204 2236 loff_t endoff, int *got, struct page **pinned_page) 2205 2237 { 2206 - int check_max, ret, err; 2238 + int _got, check_max, ret, err = 0; 2207 2239 2208 2240 retry: 2209 2241 if (endoff > 0) 2210 2242 check_max_size(&ci->vfs_inode, endoff); 2243 + _got = 0; 2211 2244 check_max = 0; 2212 - err = 0; 2213 2245 ret = wait_event_interruptible(ci->i_cap_wq, 2214 - try_get_cap_refs(ci, need, want, endoff, 2215 - got, pinned_page, 2216 - &check_max, &err)); 2246 + try_get_cap_refs(ci, need, want, endoff, 2247 + &_got, &check_max, &err)); 2217 2248 if (err) 2218 2249 ret = err; 2250 + if (ret < 0) 2251 + return ret; 2252 + 2219 2253 if (check_max) 2220 2254 goto retry; 2221 - return ret; 2255 + 2256 + if (ci->i_inline_version != CEPH_INLINE_NONE && 2257 + (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && 2258 + i_size_read(&ci->vfs_inode) > 0) { 2259 + struct page *page = find_get_page(ci->vfs_inode.i_mapping, 0); 2260 + if (page) { 2261 + if (PageUptodate(page)) { 2262 + *pinned_page = page; 2263 + goto out; 2264 + } 2265 + page_cache_release(page); 2266 + } 2267 + /* 2268 + * drop cap refs first because getattr while holding 2269 + * caps refs can cause deadlock. 2270 + */ 2271 + ceph_put_cap_refs(ci, _got); 2272 + _got = 0; 2273 + 2274 + /* getattr request will bring inline data into page cache */ 2275 + ret = __ceph_do_getattr(&ci->vfs_inode, NULL, 2276 + CEPH_STAT_CAP_INLINE_DATA, true); 2277 + if (ret < 0) 2278 + return ret; 2279 + goto retry; 2280 + } 2281 + out: 2282 + *got = _got; 2283 + return 0; 2222 2284 } 2223 2285 2224 2286 /*