| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* Fileserver-directed operation handling.
- *
- * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include "internal.h"
- static atomic_t afs_operation_debug_counter;
- /*
- * Create an operation against a volume.
- */
- struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
- {
- struct afs_operation *op;
- _enter("");
- op = kzalloc_obj(*op);
- if (!op)
- return ERR_PTR(-ENOMEM);
- if (!key) {
- key = afs_request_key(volume->cell);
- if (IS_ERR(key)) {
- kfree(op);
- return ERR_CAST(key);
- }
- } else {
- key_get(key);
- }
- op->key = key;
- op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op);
- op->net = volume->cell->net;
- op->cb_v_break = atomic_read(&volume->cb_v_break);
- op->pre_volsync.creation = volume->creation_time;
- op->pre_volsync.update = volume->update_time;
- op->debug_id = atomic_inc_return(&afs_operation_debug_counter);
- op->nr_iterations = -1;
- afs_op_set_error(op, -EDESTADDRREQ);
- _leave(" = [op=%08x]", op->debug_id);
- return op;
- }
- struct afs_io_locker {
- struct list_head link;
- struct task_struct *task;
- unsigned long have_lock;
- };
- /*
- * Unlock the I/O lock on a vnode.
- */
- static void afs_unlock_for_io(struct afs_vnode *vnode)
- {
- struct afs_io_locker *locker;
- spin_lock(&vnode->lock);
- locker = list_first_entry_or_null(&vnode->io_lock_waiters,
- struct afs_io_locker, link);
- if (locker) {
- list_del(&locker->link);
- smp_store_release(&locker->have_lock, 1); /* The unlock barrier. */
- smp_mb__after_atomic(); /* Store have_lock before task state */
- wake_up_process(locker->task);
- } else {
- clear_bit(AFS_VNODE_IO_LOCK, &vnode->flags);
- }
- spin_unlock(&vnode->lock);
- }
- /*
- * Lock the I/O lock on a vnode uninterruptibly. We can't use an ordinary
- * mutex as lockdep will complain if we unlock it in the wrong thread.
- */
- static void afs_lock_for_io(struct afs_vnode *vnode)
- {
- struct afs_io_locker myself = { .task = current, };
- spin_lock(&vnode->lock);
- if (!test_and_set_bit(AFS_VNODE_IO_LOCK, &vnode->flags)) {
- spin_unlock(&vnode->lock);
- return;
- }
- list_add_tail(&myself.link, &vnode->io_lock_waiters);
- spin_unlock(&vnode->lock);
- for (;;) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- if (smp_load_acquire(&myself.have_lock)) /* The lock barrier */
- break;
- schedule();
- }
- __set_current_state(TASK_RUNNING);
- }
- /*
- * Lock the I/O lock on a vnode interruptibly. We can't use an ordinary mutex
- * as lockdep will complain if we unlock it in the wrong thread.
- */
- static int afs_lock_for_io_interruptible(struct afs_vnode *vnode)
- {
- struct afs_io_locker myself = { .task = current, };
- int ret = 0;
- spin_lock(&vnode->lock);
- if (!test_and_set_bit(AFS_VNODE_IO_LOCK, &vnode->flags)) {
- spin_unlock(&vnode->lock);
- return 0;
- }
- list_add_tail(&myself.link, &vnode->io_lock_waiters);
- spin_unlock(&vnode->lock);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (smp_load_acquire(&myself.have_lock) || /* The lock barrier */
- signal_pending(current))
- break;
- schedule();
- }
- __set_current_state(TASK_RUNNING);
- /* If we got a signal, try to transfer the lock onto the next
- * waiter.
- */
- if (unlikely(signal_pending(current))) {
- spin_lock(&vnode->lock);
- if (myself.have_lock) {
- spin_unlock(&vnode->lock);
- afs_unlock_for_io(vnode);
- } else {
- list_del(&myself.link);
- spin_unlock(&vnode->lock);
- }
- ret = -ERESTARTSYS;
- }
- return ret;
- }
- /*
- * Lock the vnode(s) being operated upon.
- */
- static bool afs_get_io_locks(struct afs_operation *op)
- {
- struct afs_vnode *vnode = op->file[0].vnode;
- struct afs_vnode *vnode2 = op->file[1].vnode;
- _enter("");
- if (op->flags & AFS_OPERATION_UNINTR) {
- afs_lock_for_io(vnode);
- op->flags |= AFS_OPERATION_LOCK_0;
- _leave(" = t [1]");
- return true;
- }
- if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
- vnode2 = NULL;
- if (vnode2 > vnode)
- swap(vnode, vnode2);
- if (afs_lock_for_io_interruptible(vnode) < 0) {
- afs_op_set_error(op, -ERESTARTSYS);
- op->flags |= AFS_OPERATION_STOP;
- _leave(" = f [I 0]");
- return false;
- }
- op->flags |= AFS_OPERATION_LOCK_0;
- if (vnode2) {
- if (afs_lock_for_io_interruptible(vnode2) < 0) {
- afs_op_set_error(op, -ERESTARTSYS);
- op->flags |= AFS_OPERATION_STOP;
- afs_unlock_for_io(vnode);
- op->flags &= ~AFS_OPERATION_LOCK_0;
- _leave(" = f [I 1]");
- return false;
- }
- op->flags |= AFS_OPERATION_LOCK_1;
- }
- _leave(" = t [2]");
- return true;
- }
- static void afs_drop_io_locks(struct afs_operation *op)
- {
- struct afs_vnode *vnode = op->file[0].vnode;
- struct afs_vnode *vnode2 = op->file[1].vnode;
- _enter("");
- if (op->flags & AFS_OPERATION_LOCK_1)
- afs_unlock_for_io(vnode2);
- if (op->flags & AFS_OPERATION_LOCK_0)
- afs_unlock_for_io(vnode);
- }
- static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
- unsigned int index)
- {
- struct afs_vnode *vnode = vp->vnode;
- if (vnode) {
- vp->fid = vnode->fid;
- vp->dv_before = vnode->status.data_version;
- vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
- if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
- op->flags |= AFS_OPERATION_CUR_ONLY;
- if (vp->modification)
- set_bit(AFS_VNODE_MODIFYING, &vnode->flags);
- }
- if (vp->fid.vnode)
- _debug("PREP[%u] {%llx:%llu.%u}",
- index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
- }
- /*
- * Begin an operation on the fileserver.
- *
- * Fileserver operations are serialised on the server by vnode, so we serialise
- * them here also using the io_lock.
- */
- bool afs_begin_vnode_operation(struct afs_operation *op)
- {
- struct afs_vnode *vnode = op->file[0].vnode;
- ASSERT(vnode);
- _enter("");
- if (op->file[0].need_io_lock)
- if (!afs_get_io_locks(op))
- return false;
- afs_prepare_vnode(op, &op->file[0], 0);
- afs_prepare_vnode(op, &op->file[1], 1);
- op->cb_v_break = atomic_read(&op->volume->cb_v_break);
- _leave(" = true");
- return true;
- }
- /*
- * Tidy up a filesystem cursor and unlock the vnode.
- */
- void afs_end_vnode_operation(struct afs_operation *op)
- {
- _enter("");
- switch (afs_op_error(op)) {
- case -EDESTADDRREQ:
- case -EADDRNOTAVAIL:
- case -ENETUNREACH:
- case -EHOSTUNREACH:
- afs_dump_edestaddrreq(op);
- break;
- }
- afs_drop_io_locks(op);
- }
- /*
- * Wait for an in-progress operation to complete.
- */
- void afs_wait_for_operation(struct afs_operation *op)
- {
- _enter("");
- while (afs_select_fileserver(op)) {
- op->call_responded = false;
- op->call_error = 0;
- op->call_abort_code = 0;
- if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&
- op->ops->issue_yfs_rpc)
- op->ops->issue_yfs_rpc(op);
- else if (op->ops->issue_afs_rpc)
- op->ops->issue_afs_rpc(op);
- else
- op->call_error = -ENOTSUPP;
- if (op->call) {
- afs_wait_for_call_to_complete(op->call);
- op->call_abort_code = op->call->abort_code;
- op->call_error = op->call->error;
- op->call_responded = op->call->responded;
- afs_put_call(op->call);
- }
- }
- if (op->call_responded && op->server)
- set_bit(AFS_SERVER_FL_RESPONDING, &op->server->flags);
- if (!afs_op_error(op)) {
- _debug("success");
- op->ops->success(op);
- } else if (op->cumul_error.aborted) {
- if (op->ops->aborted)
- op->ops->aborted(op);
- } else {
- if (op->ops->failed)
- op->ops->failed(op);
- }
- afs_end_vnode_operation(op);
- if (!afs_op_error(op) && op->ops->edit_dir) {
- _debug("edit_dir");
- op->ops->edit_dir(op);
- }
- _leave("");
- }
- /*
- * Dispose of an operation.
- */
- int afs_put_operation(struct afs_operation *op)
- {
- struct afs_addr_list *alist;
- int i, ret = afs_op_error(op);
- _enter("op=%08x,%d", op->debug_id, ret);
- if (op->ops && op->ops->put)
- op->ops->put(op);
- if (op->file[0].modification)
- clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);
- if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)
- clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);
- if (op->file[0].put_vnode)
- iput(&op->file[0].vnode->netfs.inode);
- if (op->file[1].put_vnode)
- iput(&op->file[1].vnode->netfs.inode);
- if (op->more_files) {
- for (i = 0; i < op->nr_files - 2; i++)
- if (op->more_files[i].put_vnode)
- iput(&op->more_files[i].vnode->netfs.inode);
- kfree(op->more_files);
- }
- if (op->estate) {
- alist = op->estate->addresses;
- if (alist) {
- if (op->call_responded &&
- op->addr_index != alist->preferred &&
- test_bit(alist->preferred, &op->addr_tried))
- WRITE_ONCE(alist->preferred, op->addr_index);
- }
- }
- afs_clear_server_states(op);
- afs_put_serverlist(op->net, op->server_list);
- afs_put_volume(op->volume, afs_volume_trace_put_put_op);
- key_put(op->key);
- kfree(op);
- return ret;
- }
- int afs_do_sync_operation(struct afs_operation *op)
- {
- afs_begin_vnode_operation(op);
- afs_wait_for_operation(op);
- return afs_put_operation(op);
- }
|