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