linux/drivers/infiniband/sw/rxe/rxe_task.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
   3 * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *         Redistribution and use in source and binary forms, with or
  12 *         without modification, are permitted provided that the following
  13 *         conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include <linux/kernel.h>
  35#include <linux/interrupt.h>
  36#include <linux/hardirq.h>
  37
  38#include "rxe_task.h"
  39
  40int __rxe_do_task(struct rxe_task *task)
  41
  42{
  43        int ret;
  44
  45        while ((ret = task->func(task->arg)) == 0)
  46                ;
  47
  48        task->ret = ret;
  49
  50        return ret;
  51}
  52
  53/*
  54 * this locking is due to a potential race where
  55 * a second caller finds the task already running
  56 * but looks just after the last call to func
  57 */
  58void rxe_do_task(unsigned long data)
  59{
  60        int cont;
  61        int ret;
  62        unsigned long flags;
  63        struct rxe_task *task = (struct rxe_task *)data;
  64
  65        spin_lock_irqsave(&task->state_lock, flags);
  66        switch (task->state) {
  67        case TASK_STATE_START:
  68                task->state = TASK_STATE_BUSY;
  69                spin_unlock_irqrestore(&task->state_lock, flags);
  70                break;
  71
  72        case TASK_STATE_BUSY:
  73                task->state = TASK_STATE_ARMED;
  74                fallthrough;
  75        case TASK_STATE_ARMED:
  76                spin_unlock_irqrestore(&task->state_lock, flags);
  77                return;
  78
  79        default:
  80                spin_unlock_irqrestore(&task->state_lock, flags);
  81                pr_warn("%s failed with bad state %d\n", __func__, task->state);
  82                return;
  83        }
  84
  85        do {
  86                cont = 0;
  87                ret = task->func(task->arg);
  88
  89                spin_lock_irqsave(&task->state_lock, flags);
  90                switch (task->state) {
  91                case TASK_STATE_BUSY:
  92                        if (ret)
  93                                task->state = TASK_STATE_START;
  94                        else
  95                                cont = 1;
  96                        break;
  97
  98                /* soneone tried to run the task since the last time we called
  99                 * func, so we will call one more time regardless of the
 100                 * return value
 101                 */
 102                case TASK_STATE_ARMED:
 103                        task->state = TASK_STATE_BUSY;
 104                        cont = 1;
 105                        break;
 106
 107                default:
 108                        pr_warn("%s failed with bad state %d\n", __func__,
 109                                task->state);
 110                }
 111                spin_unlock_irqrestore(&task->state_lock, flags);
 112        } while (cont);
 113
 114        task->ret = ret;
 115}
 116
 117int rxe_init_task(void *obj, struct rxe_task *task,
 118                  void *arg, int (*func)(void *), char *name)
 119{
 120        task->obj       = obj;
 121        task->arg       = arg;
 122        task->func      = func;
 123        snprintf(task->name, sizeof(task->name), "%s", name);
 124        task->destroyed = false;
 125
 126        tasklet_init(&task->tasklet, rxe_do_task, (unsigned long)task);
 127
 128        task->state = TASK_STATE_START;
 129        spin_lock_init(&task->state_lock);
 130
 131        return 0;
 132}
 133
 134void rxe_cleanup_task(struct rxe_task *task)
 135{
 136        unsigned long flags;
 137        bool idle;
 138
 139        /*
 140         * Mark the task, then wait for it to finish. It might be
 141         * running in a non-tasklet (direct call) context.
 142         */
 143        task->destroyed = true;
 144
 145        do {
 146                spin_lock_irqsave(&task->state_lock, flags);
 147                idle = (task->state == TASK_STATE_START);
 148                spin_unlock_irqrestore(&task->state_lock, flags);
 149        } while (!idle);
 150
 151        tasklet_kill(&task->tasklet);
 152}
 153
 154void rxe_run_task(struct rxe_task *task, int sched)
 155{
 156        if (task->destroyed)
 157                return;
 158
 159        if (sched)
 160                tasklet_schedule(&task->tasklet);
 161        else
 162                rxe_do_task((unsigned long)task);
 163}
 164
 165void rxe_disable_task(struct rxe_task *task)
 166{
 167        tasklet_disable(&task->tasklet);
 168}
 169
 170void rxe_enable_task(struct rxe_task *task)
 171{
 172        tasklet_enable(&task->tasklet);
 173}
 174