1 /* 2 * (C) 2001 Clemson University and The University of Chicago 3 * (C) 2011 Omnibond Systems 4 * 5 * Changes by Acxiom Corporation to implement generic service_operation() 6 * function, Copyright Acxiom Corporation, 2005. 7 * 8 * See COPYING in top-level directory. 9 */ 10 11 /* 12 * In-kernel waitqueue operations. 13 */ 14 15 #include "protocol.h" 16 #include "orangefs-kernel.h" 17 #include "orangefs-bufmap.h" 18 19 static int wait_for_matching_downcall(struct orangefs_kernel_op_s *, long, bool); 20 static void orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s *); 21 22 /* 23 * What we do in this function is to walk the list of operations that are 24 * present in the request queue and mark them as purged. 25 * NOTE: This is called from the device close after client-core has 26 * guaranteed that no new operations could appear on the list since the 27 * client-core is anyway going to exit. 28 */ 29 void purge_waiting_ops(void) 30 { 31 struct orangefs_kernel_op_s *op; 32 33 spin_lock(&orangefs_request_list_lock); 34 list_for_each_entry(op, &orangefs_request_list, list) { 35 gossip_debug(GOSSIP_WAIT_DEBUG, 36 "pvfs2-client-core: purging op tag %llu %s\n", 37 llu(op->tag), 38 get_opname_string(op)); 39 set_op_state_purged(op); 40 gossip_debug(GOSSIP_DEV_DEBUG, 41 "%s: op:%s: op_state:%d: process:%s:\n", 42 __func__, 43 get_opname_string(op), 44 op->op_state, 45 current->comm); 46 } 47 spin_unlock(&orangefs_request_list_lock); 48 } 49 50 /* 51 * submits a ORANGEFS operation and waits for it to complete 52 * 53 * Note op->downcall.status will contain the status of the operation (in 54 * errno format), whether provided by pvfs2-client or a result of failure to 55 * service the operation. If the caller wishes to distinguish, then 56 * op->state can be checked to see if it was serviced or not. 57 * 58 * Returns contents of op->downcall.status for convenience 59 */ 60 int service_operation(struct orangefs_kernel_op_s *op, 61 const char *op_name, 62 int flags) 63 { 64 long timeout = MAX_SCHEDULE_TIMEOUT; 65 int ret = 0; 66 67 DEFINE_WAIT(wait_entry); 68 69 op->upcall.tgid = current->tgid; 70 op->upcall.pid = current->pid; 71 72 retry_servicing: 73 op->downcall.status = 0; 74 gossip_debug(GOSSIP_WAIT_DEBUG, 75 "%s: %s op:%p: process:%s: pid:%d:\n", 76 __func__, 77 op_name, 78 op, 79 current->comm, 80 current->pid); 81 82 /* 83 * If ORANGEFS_OP_NO_MUTEX was set in flags, we need to avoid 84 * acquiring the request_mutex because we're servicing a 85 * high priority remount operation and the request_mutex is 86 * already taken. 87 */ 88 if (!(flags & ORANGEFS_OP_NO_MUTEX)) { 89 if (flags & ORANGEFS_OP_INTERRUPTIBLE) 90 ret = mutex_lock_interruptible(&orangefs_request_mutex); 91 else 92 ret = mutex_lock_killable(&orangefs_request_mutex); 93 /* 94 * check to see if we were interrupted while waiting for 95 * mutex 96 */ 97 if (ret < 0) { 98 op->downcall.status = ret; 99 gossip_debug(GOSSIP_WAIT_DEBUG, 100 "%s: service_operation interrupted.\n", 101 __func__); 102 return ret; 103 } 104 } 105 106 /* queue up the operation */ 107 spin_lock(&orangefs_request_list_lock); 108 spin_lock(&op->lock); 109 set_op_state_waiting(op); 110 gossip_debug(GOSSIP_DEV_DEBUG, 111 "%s: op:%s: op_state:%d: process:%s:\n", 112 __func__, 113 get_opname_string(op), 114 op->op_state, 115 current->comm); 116 /* add high priority remount op to the front of the line. */ 117 if (flags & ORANGEFS_OP_PRIORITY) 118 list_add(&op->list, &orangefs_request_list); 119 else 120 list_add_tail(&op->list, &orangefs_request_list); 121 spin_unlock(&op->lock); 122 wake_up_interruptible(&orangefs_request_list_waitq); 123 if (!__is_daemon_in_service()) { 124 gossip_debug(GOSSIP_WAIT_DEBUG, 125 "%s:client core is NOT in service.\n", 126 __func__); 127 /* 128 * Don't wait for the userspace component to return if 129 * the filesystem is being umounted anyway. 130 */ 131 if (op->upcall.type == ORANGEFS_VFS_OP_FS_UMOUNT) 132 timeout = 0; 133 else 134 timeout = op_timeout_secs * HZ; 135 } 136 spin_unlock(&orangefs_request_list_lock); 137 138 if (!(flags & ORANGEFS_OP_NO_MUTEX)) 139 mutex_unlock(&orangefs_request_mutex); 140 141 ret = wait_for_matching_downcall(op, timeout, 142 flags & ORANGEFS_OP_INTERRUPTIBLE); 143 144 gossip_debug(GOSSIP_WAIT_DEBUG, 145 "%s: wait_for_matching_downcall returned %d for %p\n", 146 __func__, 147 ret, 148 op); 149 150 /* got matching downcall; make sure status is in errno format */ 151 if (!ret) { 152 spin_unlock(&op->lock); 153 op->downcall.status = 154 orangefs_normalize_to_errno(op->downcall.status); 155 ret = op->downcall.status; 156 goto out; 157 } 158 159 /* failed to get matching downcall */ 160 if (ret == -ETIMEDOUT) { 161 gossip_err("%s: %s -- wait timed out; aborting attempt.\n", 162 __func__, 163 op_name); 164 } 165 166 /* 167 * remove a waiting op from the request list or 168 * remove an in-progress op from the in-progress list. 169 */ 170 orangefs_clean_up_interrupted_operation(op); 171 172 op->downcall.status = ret; 173 /* retry if operation has not been serviced and if requested */ 174 if (ret == -EAGAIN) { 175 op->attempts++; 176 timeout = op_timeout_secs * HZ; 177 gossip_debug(GOSSIP_WAIT_DEBUG, 178 "orangefs: tag %llu (%s)" 179 " -- operation to be retried (%d attempt)\n", 180 llu(op->tag), 181 op_name, 182 op->attempts); 183 184 /* 185 * io ops (ops that use the shared memory buffer) have 186 * to be returned to their caller for a retry. Other ops 187 * can just be recycled here. 188 */ 189 if (!op->uses_shared_memory) 190 goto retry_servicing; 191 } 192 193 out: 194 gossip_debug(GOSSIP_WAIT_DEBUG, 195 "%s: %s returning: %d for %p.\n", 196 __func__, 197 op_name, 198 ret, 199 op); 200 return ret; 201 } 202 203 /* This can get called on an I/O op if it had a bad service_operation. */ 204 bool orangefs_cancel_op_in_progress(struct orangefs_kernel_op_s *op) 205 { 206 u64 tag = op->tag; 207 if (!op_state_in_progress(op)) 208 return false; 209 210 op->slot_to_free = op->upcall.req.io.buf_index; 211 memset(&op->upcall, 0, sizeof(op->upcall)); 212 memset(&op->downcall, 0, sizeof(op->downcall)); 213 op->upcall.type = ORANGEFS_VFS_OP_CANCEL; 214 op->upcall.req.cancel.op_tag = tag; 215 op->downcall.type = ORANGEFS_VFS_OP_INVALID; 216 op->downcall.status = -1; 217 orangefs_new_tag(op); 218 219 spin_lock(&orangefs_request_list_lock); 220 /* orangefs_request_list_lock is enough of a barrier here */ 221 if (!__is_daemon_in_service()) { 222 spin_unlock(&orangefs_request_list_lock); 223 return false; 224 } 225 spin_lock(&op->lock); 226 set_op_state_waiting(op); 227 gossip_debug(GOSSIP_DEV_DEBUG, 228 "%s: op:%s: op_state:%d: process:%s:\n", 229 __func__, 230 get_opname_string(op), 231 op->op_state, 232 current->comm); 233 list_add(&op->list, &orangefs_request_list); 234 spin_unlock(&op->lock); 235 spin_unlock(&orangefs_request_list_lock); 236 237 gossip_debug(GOSSIP_WAIT_DEBUG, 238 "Attempting ORANGEFS operation cancellation of tag %llu\n", 239 llu(tag)); 240 return true; 241 } 242 243 /* 244 * Change an op to the "given up" state and remove it from its list. 245 */ 246 static void 247 orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s *op) 248 { 249 /* 250 * handle interrupted cases depending on what state we were in when 251 * the interruption is detected. 252 * 253 * Called with op->lock held. 254 */ 255 256 /* 257 * List manipulation code elsewhere will ignore ops that 258 * have been given up upon. 259 */ 260 op->op_state |= OP_VFS_STATE_GIVEN_UP; 261 262 if (list_empty(&op->list)) { 263 /* caught copying to/from daemon */ 264 BUG_ON(op_state_serviced(op)); 265 spin_unlock(&op->lock); 266 wait_for_completion(&op->waitq); 267 } else if (op_state_waiting(op)) { 268 /* 269 * upcall hasn't been read; remove op from upcall request 270 * list. 271 */ 272 spin_unlock(&op->lock); 273 spin_lock(&orangefs_request_list_lock); 274 list_del_init(&op->list); 275 spin_unlock(&orangefs_request_list_lock); 276 gossip_debug(GOSSIP_WAIT_DEBUG, 277 "Interrupted: Removed op %p from request_list\n", 278 op); 279 } else if (op_state_in_progress(op)) { 280 /* op must be removed from the in progress htable */ 281 spin_unlock(&op->lock); 282 spin_lock(&orangefs_htable_ops_in_progress_lock); 283 list_del_init(&op->list); 284 spin_unlock(&orangefs_htable_ops_in_progress_lock); 285 gossip_debug(GOSSIP_WAIT_DEBUG, 286 "Interrupted: Removed op %p" 287 " from htable_ops_in_progress\n", 288 op); 289 } else { 290 spin_unlock(&op->lock); 291 gossip_err("interrupted operation is in a weird state 0x%x\n", 292 op->op_state); 293 } 294 reinit_completion(&op->waitq); 295 } 296 297 /* 298 * Sleeps on waitqueue waiting for matching downcall. 299 * If client-core finishes servicing, then we are good to go. 300 * else if client-core exits, we get woken up here, and retry with a timeout 301 * 302 * When this call returns to the caller, the specified op will no 303 * longer be in either the in_progress hash table or on the request list. 304 * 305 * Returns 0 on success and -errno on failure 306 * Errors are: 307 * EAGAIN in case we want the caller to requeue and try again.. 308 * EINTR/EIO/ETIMEDOUT indicating we are done trying to service this 309 * operation since client-core seems to be exiting too often 310 * or if we were interrupted. 311 * 312 * Returns with op->lock taken. 313 */ 314 static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op, 315 long timeout, 316 bool interruptible) 317 { 318 long n; 319 320 /* 321 * There's a "schedule_timeout" inside of these wait 322 * primitives, during which the op is out of the hands of the 323 * user process that needs something done and is being 324 * manipulated by the client-core process. 325 */ 326 if (interruptible) 327 n = wait_for_completion_interruptible_timeout(&op->waitq, 328 timeout); 329 else 330 n = wait_for_completion_killable_timeout(&op->waitq, timeout); 331 332 spin_lock(&op->lock); 333 334 if (op_state_serviced(op)) 335 return 0; 336 337 if (unlikely(n < 0)) { 338 gossip_debug(GOSSIP_WAIT_DEBUG, 339 "%s: operation interrupted, tag %llu, %p\n", 340 __func__, 341 llu(op->tag), 342 op); 343 return -EINTR; 344 } 345 if (op_state_purged(op)) { 346 gossip_debug(GOSSIP_WAIT_DEBUG, 347 "%s: operation purged, tag %llu, %p, %d\n", 348 __func__, 349 llu(op->tag), 350 op, 351 op->attempts); 352 return (op->attempts < ORANGEFS_PURGE_RETRY_COUNT) ? 353 -EAGAIN : 354 -EIO; 355 } 356 /* must have timed out, then... */ 357 gossip_debug(GOSSIP_WAIT_DEBUG, 358 "%s: operation timed out, tag %llu, %p, %d)\n", 359 __func__, 360 llu(op->tag), 361 op, 362 op->attempts); 363 return -ETIMEDOUT; 364 } 365