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 } 41 spin_unlock(&orangefs_request_list_lock); 42 } 43 44 /* 45 * submits a ORANGEFS operation and waits for it to complete 46 * 47 * Note op->downcall.status will contain the status of the operation (in 48 * errno format), whether provided by pvfs2-client or a result of failure to 49 * service the operation. If the caller wishes to distinguish, then 50 * op->state can be checked to see if it was serviced or not. 51 * 52 * Returns contents of op->downcall.status for convenience 53 */ 54 int service_operation(struct orangefs_kernel_op_s *op, 55 const char *op_name, 56 int flags) 57 { 58 long timeout = MAX_SCHEDULE_TIMEOUT; 59 /* flags to modify behavior */ 60 int ret = 0; 61 62 DEFINE_WAIT(wait_entry); 63 64 op->upcall.tgid = current->tgid; 65 op->upcall.pid = current->pid; 66 67 retry_servicing: 68 op->downcall.status = 0; 69 gossip_debug(GOSSIP_WAIT_DEBUG, 70 "%s: %s op:%p: process:%s: pid:%d:\n", 71 __func__, 72 op_name, 73 op, 74 current->comm, 75 current->pid); 76 77 if (!(flags & ORANGEFS_OP_NO_SEMAPHORE)) { 78 if (flags & ORANGEFS_OP_INTERRUPTIBLE) 79 ret = mutex_lock_interruptible(&request_mutex); 80 else 81 ret = mutex_lock_killable(&request_mutex); 82 /* 83 * check to see if we were interrupted while waiting for 84 * semaphore 85 */ 86 if (ret < 0) { 87 op->downcall.status = ret; 88 gossip_debug(GOSSIP_WAIT_DEBUG, 89 "orangefs: service_operation interrupted.\n"); 90 return ret; 91 } 92 } 93 94 /* queue up the operation */ 95 spin_lock(&orangefs_request_list_lock); 96 spin_lock(&op->lock); 97 set_op_state_waiting(op); 98 if (flags & ORANGEFS_OP_PRIORITY) 99 list_add(&op->list, &orangefs_request_list); 100 else 101 list_add_tail(&op->list, &orangefs_request_list); 102 spin_unlock(&op->lock); 103 wake_up_interruptible(&orangefs_request_list_waitq); 104 if (!__is_daemon_in_service()) { 105 gossip_debug(GOSSIP_WAIT_DEBUG, 106 "%s:client core is NOT in service.\n", 107 __func__); 108 timeout = op_timeout_secs * HZ; 109 } 110 spin_unlock(&orangefs_request_list_lock); 111 112 if (!(flags & ORANGEFS_OP_NO_SEMAPHORE)) 113 mutex_unlock(&request_mutex); 114 115 ret = wait_for_matching_downcall(op, timeout, 116 flags & ORANGEFS_OP_INTERRUPTIBLE); 117 118 gossip_debug(GOSSIP_WAIT_DEBUG, 119 "%s: wait_for_matching_downcall returned %d for %p\n", 120 __func__, 121 ret, 122 op); 123 124 if (!ret) { 125 spin_unlock(&op->lock); 126 /* got matching downcall; make sure status is in errno format */ 127 op->downcall.status = 128 orangefs_normalize_to_errno(op->downcall.status); 129 ret = op->downcall.status; 130 goto out; 131 } 132 133 /* failed to get matching downcall */ 134 if (ret == -ETIMEDOUT) { 135 gossip_err("orangefs: %s -- wait timed out; aborting attempt.\n", 136 op_name); 137 } 138 orangefs_clean_up_interrupted_operation(op); 139 op->downcall.status = ret; 140 /* retry if operation has not been serviced and if requested */ 141 if (ret == -EAGAIN) { 142 op->attempts++; 143 timeout = op_timeout_secs * HZ; 144 gossip_debug(GOSSIP_WAIT_DEBUG, 145 "orangefs: tag %llu (%s)" 146 " -- operation to be retried (%d attempt)\n", 147 llu(op->tag), 148 op_name, 149 op->attempts); 150 151 if (!op->uses_shared_memory) 152 /* 153 * this operation doesn't use the shared memory 154 * system 155 */ 156 goto retry_servicing; 157 } 158 159 out: 160 gossip_debug(GOSSIP_WAIT_DEBUG, 161 "orangefs: service_operation %s returning: %d for %p.\n", 162 op_name, 163 ret, 164 op); 165 return ret; 166 } 167 168 bool orangefs_cancel_op_in_progress(struct orangefs_kernel_op_s *op) 169 { 170 u64 tag = op->tag; 171 if (!op_state_in_progress(op)) 172 return false; 173 174 op->slot_to_free = op->upcall.req.io.buf_index; 175 memset(&op->upcall, 0, sizeof(op->upcall)); 176 memset(&op->downcall, 0, sizeof(op->downcall)); 177 op->upcall.type = ORANGEFS_VFS_OP_CANCEL; 178 op->upcall.req.cancel.op_tag = tag; 179 op->downcall.type = ORANGEFS_VFS_OP_INVALID; 180 op->downcall.status = -1; 181 orangefs_new_tag(op); 182 183 spin_lock(&orangefs_request_list_lock); 184 /* orangefs_request_list_lock is enough of a barrier here */ 185 if (!__is_daemon_in_service()) { 186 spin_unlock(&orangefs_request_list_lock); 187 return false; 188 } 189 spin_lock(&op->lock); 190 set_op_state_waiting(op); 191 list_add(&op->list, &orangefs_request_list); 192 spin_unlock(&op->lock); 193 spin_unlock(&orangefs_request_list_lock); 194 195 gossip_debug(GOSSIP_UTILS_DEBUG, 196 "Attempting ORANGEFS operation cancellation of tag %llu\n", 197 llu(tag)); 198 return true; 199 } 200 201 static void orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s *op) 202 { 203 /* 204 * handle interrupted cases depending on what state we were in when 205 * the interruption is detected. there is a coarse grained lock 206 * across the operation. 207 * 208 * Called with op->lock held. 209 */ 210 op->op_state |= OP_VFS_STATE_GIVEN_UP; 211 212 if (op_state_waiting(op)) { 213 /* 214 * upcall hasn't been read; remove op from upcall request 215 * list. 216 */ 217 spin_unlock(&op->lock); 218 spin_lock(&orangefs_request_list_lock); 219 list_del(&op->list); 220 spin_unlock(&orangefs_request_list_lock); 221 gossip_debug(GOSSIP_WAIT_DEBUG, 222 "Interrupted: Removed op %p from request_list\n", 223 op); 224 } else if (op_state_in_progress(op)) { 225 /* op must be removed from the in progress htable */ 226 spin_unlock(&op->lock); 227 spin_lock(&htable_ops_in_progress_lock); 228 list_del(&op->list); 229 spin_unlock(&htable_ops_in_progress_lock); 230 gossip_debug(GOSSIP_WAIT_DEBUG, 231 "Interrupted: Removed op %p" 232 " from htable_ops_in_progress\n", 233 op); 234 } else if (!op_state_serviced(op)) { 235 spin_unlock(&op->lock); 236 gossip_err("interrupted operation is in a weird state 0x%x\n", 237 op->op_state); 238 } else { 239 /* 240 * It is not intended for execution to flow here, 241 * but having this unlock here makes sparse happy. 242 */ 243 gossip_err("%s: can't get here.\n", __func__); 244 spin_unlock(&op->lock); 245 } 246 reinit_completion(&op->waitq); 247 } 248 249 /* 250 * sleeps on waitqueue waiting for matching downcall. 251 * if client-core finishes servicing, then we are good to go. 252 * else if client-core exits, we get woken up here, and retry with a timeout 253 * 254 * Post when this call returns to the caller, the specified op will no 255 * longer be on any list or htable. 256 * 257 * Returns 0 on success and -errno on failure 258 * Errors are: 259 * EAGAIN in case we want the caller to requeue and try again.. 260 * EINTR/EIO/ETIMEDOUT indicating we are done trying to service this 261 * operation since client-core seems to be exiting too often 262 * or if we were interrupted. 263 * 264 * Returns with op->lock taken. 265 */ 266 static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op, 267 long timeout, 268 bool interruptible) 269 { 270 long n; 271 272 if (interruptible) 273 n = wait_for_completion_interruptible_timeout(&op->waitq, timeout); 274 else 275 n = wait_for_completion_killable_timeout(&op->waitq, timeout); 276 277 spin_lock(&op->lock); 278 279 if (op_state_serviced(op)) 280 return 0; 281 282 if (unlikely(n < 0)) { 283 gossip_debug(GOSSIP_WAIT_DEBUG, 284 "*** %s:" 285 " operation interrupted by a signal (tag " 286 "%llu, op %p)\n", 287 __func__, 288 llu(op->tag), 289 op); 290 return -EINTR; 291 } 292 if (op_state_purged(op)) { 293 gossip_debug(GOSSIP_WAIT_DEBUG, 294 "*** %s:" 295 " operation purged (tag " 296 "%llu, %p, att %d)\n", 297 __func__, 298 llu(op->tag), 299 op, 300 op->attempts); 301 return (op->attempts < ORANGEFS_PURGE_RETRY_COUNT) ? 302 -EAGAIN : 303 -EIO; 304 } 305 /* must have timed out, then... */ 306 gossip_debug(GOSSIP_WAIT_DEBUG, 307 "*** %s:" 308 " operation timed out (tag" 309 " %llu, %p, att %d)\n", 310 __func__, 311 llu(op->tag), 312 op, 313 op->attempts); 314 return -ETIMEDOUT; 315 } 316