1 // SPDX-License-Identifier: GPL-2.0
3 * (C) 2001 Clemson University and The University of Chicago
4 * (C) 2011 Omnibond Systems
6 * Changes by Acxiom Corporation to implement generic service_operation()
7 * function, Copyright Acxiom Corporation, 2005.
9 * See COPYING in top-level directory.
13 * In-kernel waitqueue operations.
17 #include "orangefs-kernel.h"
18 #include "orangefs-bufmap.h"
20 static int wait_for_matching_downcall(struct orangefs_kernel_op_s
*, long, bool);
21 static void orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s
*);
24 * What we do in this function is to walk the list of operations that are
25 * present in the request queue and mark them as purged.
26 * NOTE: This is called from the device close after client-core has
27 * guaranteed that no new operations could appear on the list since the
28 * client-core is anyway going to exit.
30 void purge_waiting_ops(void)
32 struct orangefs_kernel_op_s
*op
;
34 spin_lock(&orangefs_request_list_lock
);
35 list_for_each_entry(op
, &orangefs_request_list
, list
) {
36 gossip_debug(GOSSIP_WAIT_DEBUG
,
37 "pvfs2-client-core: purging op tag %llu %s\n",
39 get_opname_string(op
));
40 set_op_state_purged(op
);
41 gossip_debug(GOSSIP_DEV_DEBUG
,
42 "%s: op:%s: op_state:%d: process:%s:\n",
44 get_opname_string(op
),
48 spin_unlock(&orangefs_request_list_lock
);
52 * submits a ORANGEFS operation and waits for it to complete
54 * Note op->downcall.status will contain the status of the operation (in
55 * errno format), whether provided by pvfs2-client or a result of failure to
56 * service the operation. If the caller wishes to distinguish, then
57 * op->state can be checked to see if it was serviced or not.
59 * Returns contents of op->downcall.status for convenience
61 int service_operation(struct orangefs_kernel_op_s
*op
,
65 long timeout
= MAX_SCHEDULE_TIMEOUT
;
68 DEFINE_WAIT(wait_entry
);
70 op
->upcall
.tgid
= current
->tgid
;
71 op
->upcall
.pid
= current
->pid
;
74 op
->downcall
.status
= 0;
75 gossip_debug(GOSSIP_WAIT_DEBUG
,
76 "%s: %s op:%p: process:%s: pid:%d:\n",
84 * If ORANGEFS_OP_NO_MUTEX was set in flags, we need to avoid
85 * acquiring the request_mutex because we're servicing a
86 * high priority remount operation and the request_mutex is
89 if (!(flags
& ORANGEFS_OP_NO_MUTEX
)) {
90 if (flags
& ORANGEFS_OP_INTERRUPTIBLE
)
91 ret
= mutex_lock_interruptible(&orangefs_request_mutex
);
93 ret
= mutex_lock_killable(&orangefs_request_mutex
);
95 * check to see if we were interrupted while waiting for
99 op
->downcall
.status
= ret
;
100 gossip_debug(GOSSIP_WAIT_DEBUG
,
101 "%s: service_operation interrupted.\n",
107 /* queue up the operation */
108 spin_lock(&orangefs_request_list_lock
);
109 spin_lock(&op
->lock
);
110 set_op_state_waiting(op
);
111 gossip_debug(GOSSIP_DEV_DEBUG
,
112 "%s: op:%s: op_state:%d: process:%s:\n",
114 get_opname_string(op
),
117 /* add high priority remount op to the front of the line. */
118 if (flags
& ORANGEFS_OP_PRIORITY
)
119 list_add(&op
->list
, &orangefs_request_list
);
121 list_add_tail(&op
->list
, &orangefs_request_list
);
122 spin_unlock(&op
->lock
);
123 wake_up_interruptible(&orangefs_request_list_waitq
);
124 if (!__is_daemon_in_service()) {
125 gossip_debug(GOSSIP_WAIT_DEBUG
,
126 "%s:client core is NOT in service.\n",
129 * Don't wait for the userspace component to return if
130 * the filesystem is being umounted anyway.
132 if (op
->upcall
.type
== ORANGEFS_VFS_OP_FS_UMOUNT
)
135 timeout
= op_timeout_secs
* HZ
;
137 spin_unlock(&orangefs_request_list_lock
);
139 if (!(flags
& ORANGEFS_OP_NO_MUTEX
))
140 mutex_unlock(&orangefs_request_mutex
);
142 ret
= wait_for_matching_downcall(op
, timeout
,
143 flags
& ORANGEFS_OP_INTERRUPTIBLE
);
145 gossip_debug(GOSSIP_WAIT_DEBUG
,
146 "%s: wait_for_matching_downcall returned %d for %p\n",
151 /* got matching downcall; make sure status is in errno format */
153 spin_unlock(&op
->lock
);
154 op
->downcall
.status
=
155 orangefs_normalize_to_errno(op
->downcall
.status
);
156 ret
= op
->downcall
.status
;
160 /* failed to get matching downcall */
161 if (ret
== -ETIMEDOUT
) {
162 gossip_err("%s: %s -- wait timed out; aborting attempt.\n",
168 * remove a waiting op from the request list or
169 * remove an in-progress op from the in-progress list.
171 orangefs_clean_up_interrupted_operation(op
);
173 op
->downcall
.status
= ret
;
174 /* retry if operation has not been serviced and if requested */
175 if (ret
== -EAGAIN
) {
177 timeout
= op_timeout_secs
* HZ
;
178 gossip_debug(GOSSIP_WAIT_DEBUG
,
179 "orangefs: tag %llu (%s)"
180 " -- operation to be retried (%d attempt)\n",
186 * io ops (ops that use the shared memory buffer) have
187 * to be returned to their caller for a retry. Other ops
188 * can just be recycled here.
190 if (!op
->uses_shared_memory
)
191 goto retry_servicing
;
195 gossip_debug(GOSSIP_WAIT_DEBUG
,
196 "%s: %s returning: %d for %p.\n",
204 /* This can get called on an I/O op if it had a bad service_operation. */
205 bool orangefs_cancel_op_in_progress(struct orangefs_kernel_op_s
*op
)
208 if (!op_state_in_progress(op
))
211 op
->slot_to_free
= op
->upcall
.req
.io
.buf_index
;
212 memset(&op
->upcall
, 0, sizeof(op
->upcall
));
213 memset(&op
->downcall
, 0, sizeof(op
->downcall
));
214 op
->upcall
.type
= ORANGEFS_VFS_OP_CANCEL
;
215 op
->upcall
.req
.cancel
.op_tag
= tag
;
216 op
->downcall
.type
= ORANGEFS_VFS_OP_INVALID
;
217 op
->downcall
.status
= -1;
218 orangefs_new_tag(op
);
220 spin_lock(&orangefs_request_list_lock
);
221 /* orangefs_request_list_lock is enough of a barrier here */
222 if (!__is_daemon_in_service()) {
223 spin_unlock(&orangefs_request_list_lock
);
226 spin_lock(&op
->lock
);
227 set_op_state_waiting(op
);
228 gossip_debug(GOSSIP_DEV_DEBUG
,
229 "%s: op:%s: op_state:%d: process:%s:\n",
231 get_opname_string(op
),
234 list_add(&op
->list
, &orangefs_request_list
);
235 spin_unlock(&op
->lock
);
236 spin_unlock(&orangefs_request_list_lock
);
238 gossip_debug(GOSSIP_WAIT_DEBUG
,
239 "Attempting ORANGEFS operation cancellation of tag %llu\n",
245 * Change an op to the "given up" state and remove it from its list.
248 orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s
*op
)
251 * handle interrupted cases depending on what state we were in when
252 * the interruption is detected.
254 * Called with op->lock held.
258 * List manipulation code elsewhere will ignore ops that
259 * have been given up upon.
261 op
->op_state
|= OP_VFS_STATE_GIVEN_UP
;
263 if (list_empty(&op
->list
)) {
264 /* caught copying to/from daemon */
265 BUG_ON(op_state_serviced(op
));
266 spin_unlock(&op
->lock
);
267 wait_for_completion(&op
->waitq
);
268 } else if (op_state_waiting(op
)) {
270 * upcall hasn't been read; remove op from upcall request
273 spin_unlock(&op
->lock
);
274 spin_lock(&orangefs_request_list_lock
);
275 list_del_init(&op
->list
);
276 spin_unlock(&orangefs_request_list_lock
);
277 gossip_debug(GOSSIP_WAIT_DEBUG
,
278 "Interrupted: Removed op %p from request_list\n",
280 } else if (op_state_in_progress(op
)) {
281 /* op must be removed from the in progress htable */
282 spin_unlock(&op
->lock
);
283 spin_lock(&orangefs_htable_ops_in_progress_lock
);
284 list_del_init(&op
->list
);
285 spin_unlock(&orangefs_htable_ops_in_progress_lock
);
286 gossip_debug(GOSSIP_WAIT_DEBUG
,
287 "Interrupted: Removed op %p"
288 " from htable_ops_in_progress\n",
291 spin_unlock(&op
->lock
);
292 gossip_err("interrupted operation is in a weird state 0x%x\n",
295 reinit_completion(&op
->waitq
);
299 * Sleeps on waitqueue waiting for matching downcall.
300 * If client-core finishes servicing, then we are good to go.
301 * else if client-core exits, we get woken up here, and retry with a timeout
303 * When this call returns to the caller, the specified op will no
304 * longer be in either the in_progress hash table or on the request list.
306 * Returns 0 on success and -errno on failure
308 * EAGAIN in case we want the caller to requeue and try again..
309 * EINTR/EIO/ETIMEDOUT indicating we are done trying to service this
310 * operation since client-core seems to be exiting too often
311 * or if we were interrupted.
313 * Returns with op->lock taken.
315 static int wait_for_matching_downcall(struct orangefs_kernel_op_s
*op
,
322 * There's a "schedule_timeout" inside of these wait
323 * primitives, during which the op is out of the hands of the
324 * user process that needs something done and is being
325 * manipulated by the client-core process.
328 n
= wait_for_completion_interruptible_timeout(&op
->waitq
,
331 n
= wait_for_completion_killable_timeout(&op
->waitq
, timeout
);
333 spin_lock(&op
->lock
);
335 if (op_state_serviced(op
))
338 if (unlikely(n
< 0)) {
339 gossip_debug(GOSSIP_WAIT_DEBUG
,
340 "%s: operation interrupted, tag %llu, %p\n",
346 if (op_state_purged(op
)) {
347 gossip_debug(GOSSIP_WAIT_DEBUG
,
348 "%s: operation purged, tag %llu, %p, %d\n",
353 return (op
->attempts
< ORANGEFS_PURGE_RETRY_COUNT
) ?
357 /* must have timed out, then... */
358 gossip_debug(GOSSIP_WAIT_DEBUG
,
359 "%s: operation timed out, tag %llu, %p, %d)\n",