root/branches/Orange-Branch/src/kernel/linux-2.6/waitqueue.c @ 8983

Revision 8983, 11.2 KB (checked in by mtmoore, 22 months ago)

learning to type

Line 
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/** \file
12 *  \ingroup pvfs2linux
13 *
14 *  In-kernel waitqueue operations.
15 */
16
17#include "pvfs2-kernel.h"
18#include "pvfs2-internal.h"
19
20
21/* What we do in this function is to walk the list of operations that are present
22 * in the request queue and mark them as purged.
23 * NOTE: This is called from the device close after client-core has guaranteed that no new
24 * operations could appear on the list since the client-core is anyway going to exit.
25 */
26void purge_waiting_ops(void)
27{
28    pvfs2_kernel_op_t *op;
29    spin_lock(&pvfs2_request_list_lock);
30    list_for_each_entry(op, &pvfs2_request_list, list)
31    {
32        gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2-client-core: purging op tag %lld %s\n", lld(op->tag), get_opname_string(op));
33        spin_lock(&op->lock);
34        set_op_state_purged(op);
35        spin_unlock(&op->lock);
36        wake_up_interruptible(&op->waitq);
37    }
38    spin_unlock(&pvfs2_request_list_lock);
39    return;
40}
41
42/**
43 * submits a PVFS2 operation and waits for it to complete
44 *
45 * \note op->downcall.status will contain the status of the operation (in
46 * errno format), whether provided by pvfs2-client or a result of failure to
47 * service the operation.  If the caller wishes to distinguish, then
48 * op->state can be checked to see if it was serviced or not.
49 *
50 * \returns contents of op->downcall.status for convenience
51 */
52int service_operation(
53    pvfs2_kernel_op_t* op,  /**< operation structure to process */
54    const char* op_name,    /**< string name for operation */
55    int flags)               /**< flags to modify behavior */
56{
57    sigset_t orig_sigset;
58    int ret = 0;
59    op->upcall.pid = current->pid;
60#ifdef PVFS2_LINUX_KERNEL_2_4
61    op->upcall.tgid = -1;
62#else
63    op->upcall.tgid = current->tgid;
64#endif
65
66retry_servicing:
67    op->downcall.status = 0;
68    gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2: service_operation: %s %p\n", op_name, op);
69    gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2: operation posted by process: %s, pid: %i\n", current->comm, current->pid);
70
71    /* mask out signals if this operation is not to be interrupted */
72    if(!(flags & PVFS2_OP_INTERRUPTIBLE))
73    {
74        mask_blocked_signals(&orig_sigset);
75    }
76
77    if(!(flags & PVFS2_OP_NO_SEMAPHORE))
78    {
79        ret = down_interruptible(&request_semaphore);
80        /* check to see if we were interrupted while waiting for semaphore */
81        if(ret < 0)
82        {
83            if(!(flags & PVFS2_OP_INTERRUPTIBLE))
84            {
85                unmask_blocked_signals(&orig_sigset);
86            }
87            op->downcall.status = ret;
88            gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2: service_operation interrupted.\n");
89            return(ret);
90        }
91    }
92   
93    if (is_daemon_in_service() < 0)
94    {
95        /* By incrementing the per-operation attempt counter, we directly go into the timeout logic
96         * while waiting for the matching downcall to be read
97         */
98        op->attempts++;
99    }
100
101    /* queue up the operation */
102    if(flags & PVFS2_OP_PRIORITY)
103    {
104        add_priority_op_to_request_list(op);
105    }
106    else
107    {
108        add_op_to_request_list(op);
109    }
110
111    if(!(flags & PVFS2_OP_NO_SEMAPHORE))
112    {
113        up(&request_semaphore);
114    }
115
116    /* If we are asked to service an asynchronous operation from VFS perspective, we are done */
117    if (flags & PVFS2_OP_ASYNC)
118    {
119        return 0;
120    }
121   
122    if(flags & PVFS2_OP_CANCELLATION)
123    {
124        ret = wait_for_cancellation_downcall(op);
125    }
126    else
127    {
128        ret = wait_for_matching_downcall(op);
129    }
130
131    if(ret < 0)
132    {
133        /* failed to get matching downcall */
134        if(ret == -ETIMEDOUT)
135        {
136            gossip_err("pvfs2: %s -- wait timed out; aborting attempt.\n",
137                       op_name);
138        }
139        op->downcall.status = ret;
140    }
141    else
142    {
143        /* got matching downcall; make sure status is in errno format */
144        op->downcall.status = pvfs2_normalize_to_errno(op->downcall.status);
145        ret = op->downcall.status;
146    }
147
148    if(!(flags & PVFS2_OP_INTERRUPTIBLE))
149    {
150        unmask_blocked_signals(&orig_sigset);
151    }
152
153    BUG_ON(ret != op->downcall.status);
154    /* retry if operation has not been serviced and if requested */
155    if (!op_state_serviced(op) && op->downcall.status == -EAGAIN)
156    {
157        gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2: tag %lld (%s) -- operation to be retried (%d attempt)\n",
158                lld(op->tag), op_name, op->attempts + 1);
159        goto retry_servicing;
160    }
161    gossip_debug(GOSSIP_WAIT_DEBUG, "pvfs2: service_operation %s returning: %d for %p.\n", op_name, ret, op);
162    return(ret);
163}
164
165void pvfs2_clean_up_interrupted_operation(
166    pvfs2_kernel_op_t * op)
167{
168    /*
169      handle interrupted cases depending on what state we were in when
170      the interruption is detected.  there is a coarse grained lock
171      across the operation.
172
173      NOTE: be sure not to reverse lock ordering by locking an op lock
174      while holding the request_list lock.  Here, we first lock the op
175      and then lock the appropriate list.
176    */
177    if( !op )
178    {
179        return;
180    }
181
182    /* one more sanity check, make sure it's in one of the possible states
183     * or don't try to cancel it */
184    if( ! (op_state_waiting(op) || op_state_in_progress(op) ||
185           op_state_serviced(op) || op_state_purged(op)) )
186    {
187        return;
188    }
189
190    spin_lock(&op->lock);
191
192    if (op_state_waiting(op))
193    {
194        /*
195          upcall hasn't been read; remove op from upcall request
196          list.
197        */
198        spin_unlock(&op->lock);
199        remove_op_from_request_list(op);
200        gossip_debug(GOSSIP_WAIT_DEBUG, "Interrupted: Removed op %p from request_list\n", op);
201    }
202    else if (op_state_in_progress(op))
203    {
204        /* op must be removed from the in progress htable */
205        spin_unlock(&op->lock);
206        remove_op_from_htable_ops_in_progress(op);
207        gossip_debug(GOSSIP_WAIT_DEBUG, "Interrupted: Removed op %p from "
208                    "htable_ops_in_progress\n", op);
209    }
210    else if (!op_state_serviced(op))
211    {
212        spin_unlock(&op->lock);
213        gossip_err("interrupted operation is in a weird state 0x%x\n",
214                    op->op_state);
215    }
216}
217
218/** sleeps on waitqueue waiting for matching downcall.
219 *  if client-core finishes servicing, then we are good to go.
220 *  else if client-core exits, we get woken up here, and retry with a timeout
221 *
222 *  \post when this call returns to the caller, the specified op will no
223 *        longer be on any list or htable.
224 *
225 *  \returns 0 on success and -errno on failure
226 *  Errors are:
227 *  EAGAIN in case we want the caller to requeue and try again..
228 *  EINTR/EIO/ETIMEDOUT indicating we are done trying to service this
229 *                operation since client-core seems to be exiting too often
230 *                or if we were interrupted.
231 */
232int wait_for_matching_downcall(pvfs2_kernel_op_t * op)
233{
234    int ret = -EINVAL;
235    DECLARE_WAITQUEUE(wait_entry, current);
236
237    spin_lock(&op->lock);
238    add_wait_queue(&op->waitq, &wait_entry);
239    spin_unlock(&op->lock);
240
241    while (1)
242    {
243        set_current_state(TASK_INTERRUPTIBLE);
244
245        spin_lock(&op->lock);
246        if (op_state_serviced(op))
247        {
248            spin_unlock(&op->lock);
249            ret = 0;
250            break;
251        }
252        spin_unlock(&op->lock);
253
254        if (!signal_pending(current))
255        {
256            /* if this was our first attempt and client-core has not purged our
257             * operation, we are happy to simply wait */
258            spin_lock(&op->lock);
259            if (op->attempts == 0 && !op_state_purged(op))
260            {
261                spin_unlock(&op->lock);
262                schedule();
263            }
264            else {
265                spin_unlock(&op->lock);
266                /* subsequent attempts, we retry exactly once with timeouts */
267                if (!schedule_timeout(MSECS_TO_JIFFIES(1000 * op_timeout_secs)))
268                {
269                    gossip_debug(GOSSIP_WAIT_DEBUG, "*** %s: operation timed "
270                                 "out (tag %lld, %p, att %d)\n", __func__,
271                                 lld(op->tag), op, op->attempts);
272                    ret = -ETIMEDOUT;
273                    pvfs2_clean_up_interrupted_operation(op);
274                    break;
275                }
276            }
277            spin_lock(&op->lock);
278            op->attempts++;
279            /* if the operation was purged in the meantime, it is better to
280             * requeue it afresh  but ensure that we have not been purged
281             * repeatedly. This could happen if client-core crashes when an op
282             * is being serviced, so we requeue the op, client core crashes
283             * again so we requeue the op, client core starts, and so on...*/
284            if (op_state_purged(op))
285            {
286                ret = (op->attempts < PVFS2_PURGE_RETRY_COUNT) ? -EAGAIN : -EIO;
287                spin_unlock(&op->lock);
288                gossip_debug(GOSSIP_WAIT_DEBUG, "*** %s: operation purged "
289                             "(tag %lld, %p, att %d)\n", __func__, lld(op->tag),
290                             op, op->attempts);
291                pvfs2_clean_up_interrupted_operation(op);
292                break;
293            }
294            spin_unlock(&op->lock);
295            continue;
296        }
297
298        gossip_debug(GOSSIP_WAIT_DEBUG, "*** %s: operation interrupted by a "
299                     "signal (tag %lld, op %p)\n", __func__, lld(op->tag), op);
300        pvfs2_clean_up_interrupted_operation(op);
301        ret = -EINTR;
302        break;
303    }
304
305    set_current_state(TASK_RUNNING);
306
307    spin_lock(&op->lock);
308    remove_wait_queue(&op->waitq, &wait_entry);
309    spin_unlock(&op->lock);
310
311    return ret;
312}
313
314/** similar to wait_for_matching_downcall(), but used in the special case
315 *  of I/O cancellations.
316 *
317 *  \note we need a special wait function because if this is called we already
318 *        know that a signal is pending in current and need to service the
319 *        cancellation upcall anyway.  the only way to exit this is to either
320 *        timeout or have the cancellation be serviced properly.
321*/
322int wait_for_cancellation_downcall(pvfs2_kernel_op_t * op)
323{
324    int ret = -EINVAL;
325    DECLARE_WAITQUEUE(wait_entry, current);
326
327    spin_lock(&op->lock);
328    add_wait_queue(&op->waitq, &wait_entry);
329    spin_unlock(&op->lock);
330
331    while (1)
332    {
333        set_current_state(TASK_INTERRUPTIBLE);
334
335        spin_lock(&op->lock);
336        if (op_state_serviced(op))
337        {
338            spin_unlock(&op->lock);
339            ret = 0;
340            break;
341        }
342        spin_unlock(&op->lock);
343
344        if (!schedule_timeout
345            (MSECS_TO_JIFFIES(1000 * op_timeout_secs)))
346        {
347            gossip_debug(GOSSIP_WAIT_DEBUG, "*** %s: operation timed out: "
348                         "(tag %lld, op %p)\n", __func__, lld(op->tag), op);
349            pvfs2_clean_up_interrupted_operation(op);
350            ret = -ETIMEDOUT;
351            break;
352        }
353    }
354
355    set_current_state(TASK_RUNNING);
356
357    spin_lock(&op->lock);
358    remove_wait_queue(&op->waitq, &wait_entry);
359    spin_unlock(&op->lock);
360
361    return ret;
362}
363
364
365/*
366 * Local variables:
367 *  c-indent-level: 4
368 *  c-basic-offset: 4
369 * End:
370 *
371 * vim: ts=8 sts=4 sw=4 expandtab
372 */
Note: See TracBrowser for help on using the browser.