linux/fs/afs/fs_operation.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* Fileserver-directed operation handling.
   3 *
   4 * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/slab.h>
  10#include <linux/fs.h>
  11#include "internal.h"
  12
  13static atomic_t afs_operation_debug_counter;
  14
  15/*
  16 * Create an operation against a volume.
  17 */
  18struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
  19{
  20        struct afs_operation *op;
  21
  22        _enter("");
  23
  24        op = kzalloc(sizeof(*op), GFP_KERNEL);
  25        if (!op)
  26                return ERR_PTR(-ENOMEM);
  27
  28        if (!key) {
  29                key = afs_request_key(volume->cell);
  30                if (IS_ERR(key)) {
  31                        kfree(op);
  32                        return ERR_CAST(key);
  33                }
  34        } else {
  35                key_get(key);
  36        }
  37
  38        op->key         = key;
  39        op->volume      = afs_get_volume(volume, afs_volume_trace_get_new_op);
  40        op->net         = volume->cell->net;
  41        op->cb_v_break  = volume->cb_v_break;
  42        op->debug_id    = atomic_inc_return(&afs_operation_debug_counter);
  43        op->error       = -EDESTADDRREQ;
  44        op->ac.error    = SHRT_MAX;
  45
  46        _leave(" = [op=%08x]", op->debug_id);
  47        return op;
  48}
  49
  50/*
  51 * Lock the vnode(s) being operated upon.
  52 */
  53static bool afs_get_io_locks(struct afs_operation *op)
  54{
  55        struct afs_vnode *vnode = op->file[0].vnode;
  56        struct afs_vnode *vnode2 = op->file[1].vnode;
  57
  58        _enter("");
  59
  60        if (op->flags & AFS_OPERATION_UNINTR) {
  61                mutex_lock(&vnode->io_lock);
  62                op->flags |= AFS_OPERATION_LOCK_0;
  63                _leave(" = t [1]");
  64                return true;
  65        }
  66
  67        if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
  68                vnode2 = NULL;
  69
  70        if (vnode2 > vnode)
  71                swap(vnode, vnode2);
  72
  73        if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
  74                op->error = -ERESTARTSYS;
  75                op->flags |= AFS_OPERATION_STOP;
  76                _leave(" = f [I 0]");
  77                return false;
  78        }
  79        op->flags |= AFS_OPERATION_LOCK_0;
  80
  81        if (vnode2) {
  82                if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {
  83                        op->error = -ERESTARTSYS;
  84                        op->flags |= AFS_OPERATION_STOP;
  85                        mutex_unlock(&vnode->io_lock);
  86                        op->flags &= ~AFS_OPERATION_LOCK_0;
  87                        _leave(" = f [I 1]");
  88                        return false;
  89                }
  90                op->flags |= AFS_OPERATION_LOCK_1;
  91        }
  92
  93        _leave(" = t [2]");
  94        return true;
  95}
  96
  97static void afs_drop_io_locks(struct afs_operation *op)
  98{
  99        struct afs_vnode *vnode = op->file[0].vnode;
 100        struct afs_vnode *vnode2 = op->file[1].vnode;
 101
 102        _enter("");
 103
 104        if (op->flags & AFS_OPERATION_LOCK_1)
 105                mutex_unlock(&vnode2->io_lock);
 106        if (op->flags & AFS_OPERATION_LOCK_0)
 107                mutex_unlock(&vnode->io_lock);
 108}
 109
 110static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
 111                              unsigned int index)
 112{
 113        struct afs_vnode *vnode = vp->vnode;
 114
 115        if (vnode) {
 116                vp->fid                 = vnode->fid;
 117                vp->dv_before           = vnode->status.data_version;
 118                vp->cb_break_before     = afs_calc_vnode_cb_break(vnode);
 119                if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
 120                        op->flags       |= AFS_OPERATION_CUR_ONLY;
 121                if (vp->modification)
 122                        set_bit(AFS_VNODE_MODIFYING, &vnode->flags);
 123        }
 124
 125        if (vp->fid.vnode)
 126                _debug("PREP[%u] {%llx:%llu.%u}",
 127                       index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
 128}
 129
 130/*
 131 * Begin an operation on the fileserver.
 132 *
 133 * Fileserver operations are serialised on the server by vnode, so we serialise
 134 * them here also using the io_lock.
 135 */
 136bool afs_begin_vnode_operation(struct afs_operation *op)
 137{
 138        struct afs_vnode *vnode = op->file[0].vnode;
 139
 140        ASSERT(vnode);
 141
 142        _enter("");
 143
 144        if (op->file[0].need_io_lock)
 145                if (!afs_get_io_locks(op))
 146                        return false;
 147
 148        afs_prepare_vnode(op, &op->file[0], 0);
 149        afs_prepare_vnode(op, &op->file[1], 1);
 150        op->cb_v_break = op->volume->cb_v_break;
 151        _leave(" = true");
 152        return true;
 153}
 154
 155/*
 156 * Tidy up a filesystem cursor and unlock the vnode.
 157 */
 158static void afs_end_vnode_operation(struct afs_operation *op)
 159{
 160        _enter("");
 161
 162        if (op->error == -EDESTADDRREQ ||
 163            op->error == -EADDRNOTAVAIL ||
 164            op->error == -ENETUNREACH ||
 165            op->error == -EHOSTUNREACH)
 166                afs_dump_edestaddrreq(op);
 167
 168        afs_drop_io_locks(op);
 169
 170        if (op->error == -ECONNABORTED)
 171                op->error = afs_abort_to_error(op->ac.abort_code);
 172}
 173
 174/*
 175 * Wait for an in-progress operation to complete.
 176 */
 177void afs_wait_for_operation(struct afs_operation *op)
 178{
 179        _enter("");
 180
 181        while (afs_select_fileserver(op)) {
 182                op->cb_s_break = op->server->cb_s_break;
 183                if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&
 184                    op->ops->issue_yfs_rpc)
 185                        op->ops->issue_yfs_rpc(op);
 186                else if (op->ops->issue_afs_rpc)
 187                        op->ops->issue_afs_rpc(op);
 188                else
 189                        op->ac.error = -ENOTSUPP;
 190
 191                if (op->call)
 192                        op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
 193        }
 194
 195        switch (op->error) {
 196        case 0:
 197                _debug("success");
 198                op->ops->success(op);
 199                break;
 200        case -ECONNABORTED:
 201                if (op->ops->aborted)
 202                        op->ops->aborted(op);
 203                fallthrough;
 204        default:
 205                if (op->ops->failed)
 206                        op->ops->failed(op);
 207                break;
 208        }
 209
 210        afs_end_vnode_operation(op);
 211
 212        if (op->error == 0 && op->ops->edit_dir) {
 213                _debug("edit_dir");
 214                op->ops->edit_dir(op);
 215        }
 216        _leave("");
 217}
 218
 219/*
 220 * Dispose of an operation.
 221 */
 222int afs_put_operation(struct afs_operation *op)
 223{
 224        int i, ret = op->error;
 225
 226        _enter("op=%08x,%d", op->debug_id, ret);
 227
 228        if (op->ops && op->ops->put)
 229                op->ops->put(op);
 230        if (op->file[0].modification)
 231                clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);
 232        if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)
 233                clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);
 234        if (op->file[0].put_vnode)
 235                iput(&op->file[0].vnode->vfs_inode);
 236        if (op->file[1].put_vnode)
 237                iput(&op->file[1].vnode->vfs_inode);
 238
 239        if (op->more_files) {
 240                for (i = 0; i < op->nr_files - 2; i++)
 241                        if (op->more_files[i].put_vnode)
 242                                iput(&op->more_files[i].vnode->vfs_inode);
 243                kfree(op->more_files);
 244        }
 245
 246        afs_end_cursor(&op->ac);
 247        afs_put_serverlist(op->net, op->server_list);
 248        afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op);
 249        key_put(op->key);
 250        kfree(op);
 251        return ret;
 252}
 253
 254int afs_do_sync_operation(struct afs_operation *op)
 255{
 256        afs_begin_vnode_operation(op);
 257        afs_wait_for_operation(op);
 258        return afs_put_operation(op);
 259}
 260