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

orangefs: have ..._clean_interrupted_...() wait for copy to/from daemon

* turn all those list_del(&op->list) into list_del_init()
* don't pick ops that are already given up in control device
->read()/->write_iter().
* have orangefs_clean_interrupted_operation() notice if op is currently
being copied to/from daemon (by said ->read()/->write_iter()) and
wait for that to finish.
* when we are done copying to/from daemon and find that it had been
given up while we were doing that, wake the waiting ..._clean_interrupted_...

As the result, we are guaranteed that orangefs_clean_interrupted_operation(op)
doesn't return until nobody else can see op. Moreover, we don't need to play
with op refcounts anymore.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Mike Marshall <hubcap@omnibond.com>

authored by

Al Viro and committed by
Mike Marshall
05a50a5b 5964c1b8

+21 -23
+10 -10
fs/orangefs/devorangefs-req.c
··· 58 58 next, 59 59 &htable_ops_in_progress[index], 60 60 list) { 61 - if (op->tag == tag && !op_state_purged(op)) { 61 + if (op->tag == tag && !op_state_purged(op) && 62 + !op_state_given_up(op)) { 62 63 list_del_init(&op->list); 63 - get_op(op); /* increase ref count. */ 64 64 spin_unlock(&htable_ops_in_progress_lock); 65 65 return op; 66 66 } ··· 133 133 __s32 fsid; 134 134 /* This lock is held past the end of the loop when we break. */ 135 135 spin_lock(&op->lock); 136 - if (unlikely(op_state_purged(op))) { 136 + if (unlikely(op_state_purged(op) || op_state_given_up(op))) { 137 137 spin_unlock(&op->lock); 138 138 continue; 139 139 } ··· 199 199 */ 200 200 if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) { 201 201 gossip_err("orangefs: ERROR: Current op already queued.\n"); 202 - list_del(&cur_op->list); 202 + list_del_init(&cur_op->list); 203 203 spin_unlock(&cur_op->lock); 204 204 spin_unlock(&orangefs_request_list_lock); 205 205 return -EAGAIN; 206 206 } 207 207 list_del_init(&cur_op->list); 208 - get_op(op); 209 208 spin_unlock(&orangefs_request_list_lock); 210 209 211 210 spin_unlock(&cur_op->lock); ··· 229 230 if (unlikely(op_state_given_up(cur_op))) { 230 231 spin_unlock(&cur_op->lock); 231 232 spin_unlock(&htable_ops_in_progress_lock); 232 - op_release(cur_op); 233 + complete(&cur_op->waitq); 233 234 goto restart; 234 235 } 235 236 ··· 241 242 orangefs_devreq_add_op(cur_op); 242 243 spin_unlock(&cur_op->lock); 243 244 spin_unlock(&htable_ops_in_progress_lock); 244 - op_release(cur_op); 245 245 246 246 /* The client only asks to read one size buffer. */ 247 247 return MAX_DEV_REQ_UPSIZE; ··· 256 258 if (likely(!op_state_given_up(cur_op))) { 257 259 set_op_state_waiting(cur_op); 258 260 list_add(&cur_op->list, &orangefs_request_list); 261 + spin_unlock(&cur_op->lock); 262 + } else { 263 + spin_unlock(&cur_op->lock); 264 + complete(&cur_op->waitq); 259 265 } 260 - spin_unlock(&cur_op->lock); 261 266 spin_unlock(&orangefs_request_list_lock); 262 - op_release(cur_op); 263 267 return -EFAULT; 264 268 } 265 269 ··· 405 405 put_cancel(op); 406 406 } else if (unlikely(op_state_given_up(op))) { 407 407 spin_unlock(&op->lock); 408 + complete(&op->waitq); 408 409 } else { 409 410 set_op_state_serviced(op); 410 411 spin_unlock(&op->lock); 411 412 } 412 - op_release(op); 413 413 return ret; 414 414 415 415 Efault:
+1 -1
fs/orangefs/orangefs-kernel.h
··· 259 259 { 260 260 spin_lock(&op->lock); 261 261 if (unlikely(op_is_cancel(op))) { 262 - list_del(&op->list); 262 + list_del_init(&op->list); 263 263 spin_unlock(&op->lock); 264 264 put_cancel(op); 265 265 } else {
+10 -12
fs/orangefs/waitqueue.c
··· 208 208 * Called with op->lock held. 209 209 */ 210 210 op->op_state |= OP_VFS_STATE_GIVEN_UP; 211 - 212 - if (op_state_waiting(op)) { 211 + /* from that point on it can't be moved by anybody else */ 212 + if (list_empty(&op->list)) { 213 + /* caught copying to/from daemon */ 214 + BUG_ON(op_state_serviced(op)); 215 + spin_unlock(&op->lock); 216 + wait_for_completion(&op->waitq); 217 + } else if (op_state_waiting(op)) { 213 218 /* 214 219 * upcall hasn't been read; remove op from upcall request 215 220 * list. 216 221 */ 217 222 spin_unlock(&op->lock); 218 223 spin_lock(&orangefs_request_list_lock); 219 - list_del(&op->list); 224 + list_del_init(&op->list); 220 225 spin_unlock(&orangefs_request_list_lock); 221 226 gossip_debug(GOSSIP_WAIT_DEBUG, 222 227 "Interrupted: Removed op %p from request_list\n", ··· 230 225 /* op must be removed from the in progress htable */ 231 226 spin_unlock(&op->lock); 232 227 spin_lock(&htable_ops_in_progress_lock); 233 - list_del(&op->list); 228 + list_del_init(&op->list); 234 229 spin_unlock(&htable_ops_in_progress_lock); 235 230 gossip_debug(GOSSIP_WAIT_DEBUG, 236 231 "Interrupted: Removed op %p" 237 232 " from htable_ops_in_progress\n", 238 233 op); 239 - } else if (!op_state_serviced(op)) { 234 + } else { 240 235 spin_unlock(&op->lock); 241 236 gossip_err("interrupted operation is in a weird state 0x%x\n", 242 237 op->op_state); 243 - } else { 244 - /* 245 - * It is not intended for execution to flow here, 246 - * but having this unlock here makes sparse happy. 247 - */ 248 - gossip_err("%s: can't get here.\n", __func__); 249 - spin_unlock(&op->lock); 250 238 } 251 239 reinit_completion(&op->waitq); 252 240 }