279 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* 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 = atomic_read(&volume->cb_v_break);42 op->pre_volsync.creation = volume->creation_time;43 op->pre_volsync.update = volume->update_time;44 op->debug_id = atomic_inc_return(&afs_operation_debug_counter);45 op->nr_iterations = -1;46 afs_op_set_error(op, -EDESTADDRREQ);47 48 _leave(" = [op=%08x]", op->debug_id);49 return op;50}51 52/*53 * Lock the vnode(s) being operated upon.54 */55static bool afs_get_io_locks(struct afs_operation *op)56{57 struct afs_vnode *vnode = op->file[0].vnode;58 struct afs_vnode *vnode2 = op->file[1].vnode;59 60 _enter("");61 62 if (op->flags & AFS_OPERATION_UNINTR) {63 mutex_lock(&vnode->io_lock);64 op->flags |= AFS_OPERATION_LOCK_0;65 _leave(" = t [1]");66 return true;67 }68 69 if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)70 vnode2 = NULL;71 72 if (vnode2 > vnode)73 swap(vnode, vnode2);74 75 if (mutex_lock_interruptible(&vnode->io_lock) < 0) {76 afs_op_set_error(op, -ERESTARTSYS);77 op->flags |= AFS_OPERATION_STOP;78 _leave(" = f [I 0]");79 return false;80 }81 op->flags |= AFS_OPERATION_LOCK_0;82 83 if (vnode2) {84 if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {85 afs_op_set_error(op, -ERESTARTSYS);86 op->flags |= AFS_OPERATION_STOP;87 mutex_unlock(&vnode->io_lock);88 op->flags &= ~AFS_OPERATION_LOCK_0;89 _leave(" = f [I 1]");90 return false;91 }92 op->flags |= AFS_OPERATION_LOCK_1;93 }94 95 _leave(" = t [2]");96 return true;97}98 99static void afs_drop_io_locks(struct afs_operation *op)100{101 struct afs_vnode *vnode = op->file[0].vnode;102 struct afs_vnode *vnode2 = op->file[1].vnode;103 104 _enter("");105 106 if (op->flags & AFS_OPERATION_LOCK_1)107 mutex_unlock(&vnode2->io_lock);108 if (op->flags & AFS_OPERATION_LOCK_0)109 mutex_unlock(&vnode->io_lock);110}111 112static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,113 unsigned int index)114{115 struct afs_vnode *vnode = vp->vnode;116 117 if (vnode) {118 vp->fid = vnode->fid;119 vp->dv_before = vnode->status.data_version;120 vp->cb_break_before = afs_calc_vnode_cb_break(vnode);121 if (vnode->lock_state != AFS_VNODE_LOCK_NONE)122 op->flags |= AFS_OPERATION_CUR_ONLY;123 if (vp->modification)124 set_bit(AFS_VNODE_MODIFYING, &vnode->flags);125 }126 127 if (vp->fid.vnode)128 _debug("PREP[%u] {%llx:%llu.%u}",129 index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);130}131 132/*133 * Begin an operation on the fileserver.134 *135 * Fileserver operations are serialised on the server by vnode, so we serialise136 * them here also using the io_lock.137 */138bool afs_begin_vnode_operation(struct afs_operation *op)139{140 struct afs_vnode *vnode = op->file[0].vnode;141 142 ASSERT(vnode);143 144 _enter("");145 146 if (op->file[0].need_io_lock)147 if (!afs_get_io_locks(op))148 return false;149 150 afs_prepare_vnode(op, &op->file[0], 0);151 afs_prepare_vnode(op, &op->file[1], 1);152 op->cb_v_break = atomic_read(&op->volume->cb_v_break);153 _leave(" = true");154 return true;155}156 157/*158 * Tidy up a filesystem cursor and unlock the vnode.159 */160static void afs_end_vnode_operation(struct afs_operation *op)161{162 _enter("");163 164 switch (afs_op_error(op)) {165 case -EDESTADDRREQ:166 case -EADDRNOTAVAIL:167 case -ENETUNREACH:168 case -EHOSTUNREACH:169 afs_dump_edestaddrreq(op);170 break;171 }172 173 afs_drop_io_locks(op);174}175 176/*177 * Wait for an in-progress operation to complete.178 */179void afs_wait_for_operation(struct afs_operation *op)180{181 _enter("");182 183 while (afs_select_fileserver(op)) {184 op->call_responded = false;185 op->call_error = 0;186 op->call_abort_code = 0;187 if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&188 op->ops->issue_yfs_rpc)189 op->ops->issue_yfs_rpc(op);190 else if (op->ops->issue_afs_rpc)191 op->ops->issue_afs_rpc(op);192 else193 op->call_error = -ENOTSUPP;194 195 if (op->call) {196 afs_wait_for_call_to_complete(op->call);197 op->call_abort_code = op->call->abort_code;198 op->call_error = op->call->error;199 op->call_responded = op->call->responded;200 afs_put_call(op->call);201 }202 }203 204 if (op->call_responded && op->server)205 set_bit(AFS_SERVER_FL_RESPONDING, &op->server->flags);206 207 if (!afs_op_error(op)) {208 _debug("success");209 op->ops->success(op);210 } else if (op->cumul_error.aborted) {211 if (op->ops->aborted)212 op->ops->aborted(op);213 } else {214 if (op->ops->failed)215 op->ops->failed(op);216 }217 218 afs_end_vnode_operation(op);219 220 if (!afs_op_error(op) && op->ops->edit_dir) {221 _debug("edit_dir");222 op->ops->edit_dir(op);223 }224 _leave("");225}226 227/*228 * Dispose of an operation.229 */230int afs_put_operation(struct afs_operation *op)231{232 struct afs_addr_list *alist;233 int i, ret = afs_op_error(op);234 235 _enter("op=%08x,%d", op->debug_id, ret);236 237 if (op->ops && op->ops->put)238 op->ops->put(op);239 if (op->file[0].modification)240 clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);241 if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)242 clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);243 if (op->file[0].put_vnode)244 iput(&op->file[0].vnode->netfs.inode);245 if (op->file[1].put_vnode)246 iput(&op->file[1].vnode->netfs.inode);247 248 if (op->more_files) {249 for (i = 0; i < op->nr_files - 2; i++)250 if (op->more_files[i].put_vnode)251 iput(&op->more_files[i].vnode->netfs.inode);252 kfree(op->more_files);253 }254 255 if (op->estate) {256 alist = op->estate->addresses;257 if (alist) {258 if (op->call_responded &&259 op->addr_index != alist->preferred &&260 test_bit(alist->preferred, &op->addr_tried))261 WRITE_ONCE(alist->preferred, op->addr_index);262 }263 }264 265 afs_clear_server_states(op);266 afs_put_serverlist(op->net, op->server_list);267 afs_put_volume(op->volume, afs_volume_trace_put_put_op);268 key_put(op->key);269 kfree(op);270 return ret;271}272 273int afs_do_sync_operation(struct afs_operation *op)274{275 afs_begin_vnode_operation(op);276 afs_wait_for_operation(op);277 return afs_put_operation(op);278}279