linux/fs/orangefs/waitqueue.c
<<
>>
Prefs
   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
  19static int wait_for_matching_downcall(struct orangefs_kernel_op_s *, long, bool);
  20static 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 */
  29void 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 */
  60int 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
  72retry_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                timeout = op_timeout_secs * HZ;
 128        }
 129        spin_unlock(&orangefs_request_list_lock);
 130
 131        if (!(flags & ORANGEFS_OP_NO_MUTEX))
 132                mutex_unlock(&orangefs_request_mutex);
 133
 134        ret = wait_for_matching_downcall(op, timeout,
 135                                         flags & ORANGEFS_OP_INTERRUPTIBLE);
 136
 137        gossip_debug(GOSSIP_WAIT_DEBUG,
 138                     "%s: wait_for_matching_downcall returned %d for %p\n",
 139                     __func__,
 140                     ret,
 141                     op);
 142
 143        /* got matching downcall; make sure status is in errno format */
 144        if (!ret) {
 145                spin_unlock(&op->lock);
 146                op->downcall.status =
 147                    orangefs_normalize_to_errno(op->downcall.status);
 148                ret = op->downcall.status;
 149                goto out;
 150        }
 151
 152        /* failed to get matching downcall */
 153        if (ret == -ETIMEDOUT) {
 154                gossip_err("%s: %s -- wait timed out; aborting attempt.\n",
 155                           __func__,
 156                           op_name);
 157        }
 158
 159        /*
 160         * remove a waiting op from the request list or
 161         * remove an in-progress op from the in-progress list.
 162         */
 163        orangefs_clean_up_interrupted_operation(op);
 164
 165        op->downcall.status = ret;
 166        /* retry if operation has not been serviced and if requested */
 167        if (ret == -EAGAIN) {
 168                op->attempts++;
 169                timeout = op_timeout_secs * HZ;
 170                gossip_debug(GOSSIP_WAIT_DEBUG,
 171                             "orangefs: tag %llu (%s)"
 172                             " -- operation to be retried (%d attempt)\n",
 173                             llu(op->tag),
 174                             op_name,
 175                             op->attempts);
 176
 177                /*
 178                 * io ops (ops that use the shared memory buffer) have
 179                 * to be returned to their caller for a retry. Other ops
 180                 * can just be recycled here.
 181                 */
 182                if (!op->uses_shared_memory)
 183                        goto retry_servicing;
 184        }
 185
 186out:
 187        gossip_debug(GOSSIP_WAIT_DEBUG,
 188                     "%s: %s returning: %d for %p.\n",
 189                     __func__,
 190                     op_name,
 191                     ret,
 192                     op);
 193        return ret;
 194}
 195
 196/* This can get called on an I/O op if it had a bad service_operation. */
 197bool orangefs_cancel_op_in_progress(struct orangefs_kernel_op_s *op)
 198{
 199        u64 tag = op->tag;
 200        if (!op_state_in_progress(op))
 201                return false;
 202
 203        op->slot_to_free = op->upcall.req.io.buf_index;
 204        memset(&op->upcall, 0, sizeof(op->upcall));
 205        memset(&op->downcall, 0, sizeof(op->downcall));
 206        op->upcall.type = ORANGEFS_VFS_OP_CANCEL;
 207        op->upcall.req.cancel.op_tag = tag;
 208        op->downcall.type = ORANGEFS_VFS_OP_INVALID;
 209        op->downcall.status = -1;
 210        orangefs_new_tag(op);
 211
 212        spin_lock(&orangefs_request_list_lock);
 213        /* orangefs_request_list_lock is enough of a barrier here */
 214        if (!__is_daemon_in_service()) {
 215                spin_unlock(&orangefs_request_list_lock);
 216                return false;
 217        }
 218        spin_lock(&op->lock);
 219        set_op_state_waiting(op);
 220        gossip_debug(GOSSIP_DEV_DEBUG,
 221                     "%s: op:%s: op_state:%d: process:%s:\n",
 222                     __func__,
 223                     get_opname_string(op),
 224                     op->op_state,
 225                     current->comm);
 226        list_add(&op->list, &orangefs_request_list);
 227        spin_unlock(&op->lock);
 228        spin_unlock(&orangefs_request_list_lock);
 229
 230        gossip_debug(GOSSIP_WAIT_DEBUG,
 231                     "Attempting ORANGEFS operation cancellation of tag %llu\n",
 232                     llu(tag));
 233        return true;
 234}
 235
 236/*
 237 * Change an op to the "given up" state and remove it from its list.
 238 */
 239static void
 240        orangefs_clean_up_interrupted_operation(struct orangefs_kernel_op_s *op)
 241{
 242        /*
 243         * handle interrupted cases depending on what state we were in when
 244         * the interruption is detected.
 245         *
 246         * Called with op->lock held.
 247         */
 248
 249        /*
 250         * List manipulation code elsewhere will ignore ops that
 251         * have been given up upon.
 252         */
 253        op->op_state |= OP_VFS_STATE_GIVEN_UP;
 254
 255        if (list_empty(&op->list)) {
 256                /* caught copying to/from daemon */
 257                BUG_ON(op_state_serviced(op));
 258                spin_unlock(&op->lock);
 259                wait_for_completion(&op->waitq);
 260        } else if (op_state_waiting(op)) {
 261                /*
 262                 * upcall hasn't been read; remove op from upcall request
 263                 * list.
 264                 */
 265                spin_unlock(&op->lock);
 266                spin_lock(&orangefs_request_list_lock);
 267                list_del_init(&op->list);
 268                spin_unlock(&orangefs_request_list_lock);
 269                gossip_debug(GOSSIP_WAIT_DEBUG,
 270                             "Interrupted: Removed op %p from request_list\n",
 271                             op);
 272        } else if (op_state_in_progress(op)) {
 273                /* op must be removed from the in progress htable */
 274                spin_unlock(&op->lock);
 275                spin_lock(&orangefs_htable_ops_in_progress_lock);
 276                list_del_init(&op->list);
 277                spin_unlock(&orangefs_htable_ops_in_progress_lock);
 278                gossip_debug(GOSSIP_WAIT_DEBUG,
 279                             "Interrupted: Removed op %p"
 280                             " from htable_ops_in_progress\n",
 281                             op);
 282        } else {
 283                spin_unlock(&op->lock);
 284                gossip_err("interrupted operation is in a weird state 0x%x\n",
 285                           op->op_state);
 286        }
 287        reinit_completion(&op->waitq);
 288}
 289
 290/*
 291 * Sleeps on waitqueue waiting for matching downcall.
 292 * If client-core finishes servicing, then we are good to go.
 293 * else if client-core exits, we get woken up here, and retry with a timeout
 294 *
 295 * When this call returns to the caller, the specified op will no
 296 * longer be in either the in_progress hash table or on the request list.
 297 *
 298 * Returns 0 on success and -errno on failure
 299 * Errors are:
 300 * EAGAIN in case we want the caller to requeue and try again..
 301 * EINTR/EIO/ETIMEDOUT indicating we are done trying to service this
 302 * operation since client-core seems to be exiting too often
 303 * or if we were interrupted.
 304 *
 305 * Returns with op->lock taken.
 306 */
 307static int wait_for_matching_downcall(struct orangefs_kernel_op_s *op,
 308                                      long timeout,
 309                                      bool interruptible)
 310{
 311        long n;
 312
 313        /*
 314         * There's a "schedule_timeout" inside of these wait
 315         * primitives, during which the op is out of the hands of the
 316         * user process that needs something done and is being
 317         * manipulated by the client-core process.
 318         */
 319        if (interruptible)
 320                n = wait_for_completion_interruptible_timeout(&op->waitq,
 321                                                              timeout);
 322        else
 323                n = wait_for_completion_killable_timeout(&op->waitq, timeout);
 324
 325        spin_lock(&op->lock);
 326
 327        if (op_state_serviced(op))
 328                return 0;
 329
 330        if (unlikely(n < 0)) {
 331                gossip_debug(GOSSIP_WAIT_DEBUG,
 332                             "%s: operation interrupted, tag %llu, %p\n",
 333                             __func__,
 334                             llu(op->tag),
 335                             op);
 336                return -EINTR;
 337        }
 338        if (op_state_purged(op)) {
 339                gossip_debug(GOSSIP_WAIT_DEBUG,
 340                             "%s: operation purged, tag %llu, %p, %d\n",
 341                             __func__,
 342                             llu(op->tag),
 343                             op,
 344                             op->attempts);
 345                return (op->attempts < ORANGEFS_PURGE_RETRY_COUNT) ?
 346                         -EAGAIN :
 347                         -EIO;
 348        }
 349        /* must have timed out, then... */
 350        gossip_debug(GOSSIP_WAIT_DEBUG,
 351                     "%s: operation timed out, tag %llu, %p, %d)\n",
 352                     __func__,
 353                     llu(op->tag),
 354                     op,
 355                     op->attempts);
 356        return -ETIMEDOUT;
 357}
 358