#define AFS_LOCK_GRANTED 0
#define AFS_LOCK_PENDING 1
+ #define AFS_LOCK_YOUR_TRY 2
struct workqueue_struct *afs_lock_manager;
+ static void afs_next_locker(struct afs_vnode *vnode, int error);
static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl);
static void afs_fl_release_private(struct file_lock *fl);
.fl_release_private = afs_fl_release_private,
};
+ static inline void afs_set_lock_state(struct afs_vnode *vnode, enum afs_lock_state state)
+ {
+ _debug("STATE %u -> %u", vnode->lock_state, state);
+ vnode->lock_state = state;
+ }
+
+ static atomic_t afs_file_lock_debug_id;
+
/*
* if the callback is broken on this vnode, then the lock may now be available
*/
{
_enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode);
- queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0);
+ if (vnode->lock_state != AFS_VNODE_LOCK_WAITING_FOR_CB)
+ return;
+
+ spin_lock(&vnode->lock);
+ if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB)
+ afs_next_locker(vnode, 0);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_callback_break, 0);
+ spin_unlock(&vnode->lock);
}
/*
*/
static void afs_schedule_lock_extension(struct afs_vnode *vnode)
{
- queue_delayed_work(afs_lock_manager, &vnode->lock_work,
- AFS_LOCKWAIT * HZ / 2);
+ ktime_t expires_at, now, duration;
+ u64 duration_j;
+
+ expires_at = ktime_add_ms(vnode->locked_at, AFS_LOCKWAIT * 1000 / 2);
+ now = ktime_get_real();
+ duration = ktime_sub(expires_at, now);
+ if (duration <= 0)
+ duration_j = 0;
+ else
+ duration_j = nsecs_to_jiffies(ktime_to_ns(duration));
+
+ queue_delayed_work(afs_lock_manager, &vnode->lock_work, duration_j);
+ }
+
+ /*
+ * In the case of successful completion of a lock operation, record the time
+ * the reply appeared and start the lock extension timer.
+ */
+ void afs_lock_op_done(struct afs_call *call)
+ {
+ struct afs_vnode *vnode = call->reply[0];
+
+ if (call->error == 0) {
+ spin_lock(&vnode->lock);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_timestamp, 0);
+ vnode->locked_at = call->reply_time;
+ afs_schedule_lock_extension(vnode);
+ spin_unlock(&vnode->lock);
+ }
}
/*
* first lock in the queue is itself a readlock)
* - the caller must hold the vnode lock
*/
- static void afs_grant_locks(struct afs_vnode *vnode, struct file_lock *fl)
+ static void afs_grant_locks(struct afs_vnode *vnode)
{
struct file_lock *p, *_p;
+ bool exclusive = (vnode->lock_type == AFS_LOCK_WRITE);
- list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
- if (fl->fl_type == F_RDLCK) {
- list_for_each_entry_safe(p, _p, &vnode->pending_locks,
- fl_u.afs.link) {
- if (p->fl_type == F_RDLCK) {
- p->fl_u.afs.state = AFS_LOCK_GRANTED;
- list_move_tail(&p->fl_u.afs.link,
- &vnode->granted_locks);
- wake_up(&p->fl_wait);
- }
+ list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) {
+ if (!exclusive && p->fl_type == F_WRLCK)
+ continue;
+
+ list_move_tail(&p->fl_u.afs.link, &vnode->granted_locks);
+ p->fl_u.afs.state = AFS_LOCK_GRANTED;
+ trace_afs_flock_op(vnode, p, afs_flock_op_grant);
+ wake_up(&p->fl_wait);
+ }
+ }
+
+ /*
+ * If an error is specified, reject every pending lock that matches the
+ * authentication and type of the lock we failed to get. If there are any
+ * remaining lockers, try to wake up one of them to have a go.
+ */
+ static void afs_next_locker(struct afs_vnode *vnode, int error)
+ {
+ struct file_lock *p, *_p, *next = NULL;
+ struct key *key = vnode->lock_key;
+ unsigned int fl_type = F_RDLCK;
+
+ _enter("");
+
+ if (vnode->lock_type == AFS_LOCK_WRITE)
+ fl_type = F_WRLCK;
+
+ list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) {
+ if (error &&
+ p->fl_type == fl_type &&
+ afs_file_key(p->fl_file) == key) {
+ list_del_init(&p->fl_u.afs.link);
+ p->fl_u.afs.state = error;
+ wake_up(&p->fl_wait);
}
+
+ /* Select the next locker to hand off to. */
+ if (next &&
+ (next->fl_type == F_WRLCK || p->fl_type == F_RDLCK))
+ continue;
+ next = p;
}
+
+ vnode->lock_key = NULL;
+ key_put(key);
+
+ if (next) {
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING);
+ next->fl_u.afs.state = AFS_LOCK_YOUR_TRY;
+ trace_afs_flock_op(vnode, next, afs_flock_op_wake);
+ wake_up(&next->fl_wait);
+ } else {
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_NONE);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_no_lockers, 0);
+ }
+
+ _leave("");
+ }
+
+ /*
+ * Kill off all waiters in the the pending lock queue due to the vnode being
+ * deleted.
+ */
+ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
+ {
+ struct file_lock *p;
+
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_DELETED);
+
+ while (!list_empty(&vnode->pending_locks)) {
+ p = list_entry(vnode->pending_locks.next,
+ struct file_lock, fl_u.afs.link);
+ list_del_init(&p->fl_u.afs.link);
+ p->fl_u.afs.state = -ENOENT;
+ wake_up(&p->fl_wait);
+ }
+
+ key_put(vnode->lock_key);
+ vnode->lock_key = NULL;
}
/*
{
struct afs_vnode *vnode =
container_of(work, struct afs_vnode, lock_work.work);
- struct file_lock *fl, *next;
- afs_lock_type_t type;
struct key *key;
int ret;
_debug("wstate %u for %p", vnode->lock_state, vnode);
switch (vnode->lock_state) {
case AFS_VNODE_LOCK_NEED_UNLOCK:
- _debug("unlock");
- vnode->lock_state = AFS_VNODE_LOCK_UNLOCKING;
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_UNLOCKING);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_work_unlocking, 0);
spin_unlock(&vnode->lock);
/* attempt to release the server lock; if it fails, we just
* wait 5 minutes and it'll expire anyway */
ret = afs_release_lock(vnode, vnode->lock_key);
- if (ret < 0)
+ if (ret < 0 && vnode->lock_state != AFS_VNODE_LOCK_DELETED) {
+ trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail,
+ ret);
printk(KERN_WARNING "AFS:"
" Failed to release lock on {%llx:%llx} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
-
- spin_lock(&vnode->lock);
- key_put(vnode->lock_key);
- vnode->lock_key = NULL;
- vnode->lock_state = AFS_VNODE_LOCK_NONE;
-
- if (list_empty(&vnode->pending_locks)) {
- spin_unlock(&vnode->lock);
- return;
}
- /* The new front of the queue now owns the state variables. */
- next = list_entry(vnode->pending_locks.next,
- struct file_lock, fl_u.afs.link);
- vnode->lock_key = key_get(afs_file_key(next->fl_file));
- vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
- vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
- goto again;
+ spin_lock(&vnode->lock);
+ if (ret == -ENOENT)
+ afs_kill_lockers_enoent(vnode);
+ else
+ afs_next_locker(vnode, 0);
+ spin_unlock(&vnode->lock);
+ return;
/* If we've already got a lock, then it must be time to extend that
* lock as AFS locks time out after 5 minutes.
ASSERT(!list_empty(&vnode->granted_locks));
key = key_get(vnode->lock_key);
- vnode->lock_state = AFS_VNODE_LOCK_EXTENDING;
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_EXTENDING);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_work_extending, 0);
spin_unlock(&vnode->lock);
ret = afs_extend_lock(vnode, key); /* RPC */
key_put(key);
- if (ret < 0)
+ if (ret < 0) {
+ trace_afs_flock_ev(vnode, NULL, afs_flock_extend_fail,
+ ret);
pr_warning("AFS: Failed to extend lock on {%llx:%llx} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
+ }
spin_lock(&vnode->lock);
+ if (ret == -ENOENT) {
+ afs_kill_lockers_enoent(vnode);
+ spin_unlock(&vnode->lock);
+ return;
+ }
+
if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING)
goto again;
- vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED);
- if (ret == 0)
- afs_schedule_lock_extension(vnode);
- else
+ if (ret != 0)
queue_delayed_work(afs_lock_manager, &vnode->lock_work,
HZ * 10);
spin_unlock(&vnode->lock);
_leave(" [ext]");
return;
- /* If we don't have a granted lock, then we must've been called
- * back by the server, and so if might be possible to get a
- * lock we're currently waiting for.
- */
+ /* If we're waiting for a callback to indicate lock release, we can't
+ * actually rely on this, so need to recheck at regular intervals. The
+ * problem is that the server might not notify us if the lock just
+ * expires (say because a client died) rather than being explicitly
+ * released.
+ */
case AFS_VNODE_LOCK_WAITING_FOR_CB:
- _debug("get");
-
- key = key_get(vnode->lock_key);
- type = vnode->lock_type;
- vnode->lock_state = AFS_VNODE_LOCK_SETTING;
+ _debug("retry");
+ afs_next_locker(vnode, 0);
spin_unlock(&vnode->lock);
+ return;
- ret = afs_set_lock(vnode, key, type); /* RPC */
- key_put(key);
-
- spin_lock(&vnode->lock);
- switch (ret) {
- case -EWOULDBLOCK:
- _debug("blocked");
- break;
- case 0:
- _debug("acquired");
- vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
- /* Fall through */
- default:
- /* Pass the lock or the error onto the first locker in
- * the list - if they're looking for this type of lock.
- * If they're not, we assume that whoever asked for it
- * took a signal.
- */
- if (list_empty(&vnode->pending_locks)) {
- _debug("withdrawn");
- vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
- goto again;
- }
-
- fl = list_entry(vnode->pending_locks.next,
- struct file_lock, fl_u.afs.link);
- type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
- if (vnode->lock_type != type) {
- _debug("changed");
- vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
- goto again;
- }
-
- fl->fl_u.afs.state = ret;
- if (ret == 0)
- afs_grant_locks(vnode, fl);
- else
- list_del_init(&fl->fl_u.afs.link);
- wake_up(&fl->fl_wait);
- spin_unlock(&vnode->lock);
- _leave(" [granted]");
- return;
- }
+ case AFS_VNODE_LOCK_DELETED:
+ afs_kill_lockers_enoent(vnode);
+ spin_unlock(&vnode->lock);
+ return;
+ /* Fall through */
default:
/* Looks like a lock request was withdrawn. */
spin_unlock(&vnode->lock);
*/
static void afs_defer_unlock(struct afs_vnode *vnode)
{
- _enter("");
+ _enter("%u", vnode->lock_state);
- if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED ||
- vnode->lock_state == AFS_VNODE_LOCK_EXTENDING) {
+ if (list_empty(&vnode->granted_locks) &&
+ (vnode->lock_state == AFS_VNODE_LOCK_GRANTED ||
+ vnode->lock_state == AFS_VNODE_LOCK_EXTENDING)) {
cancel_delayed_work(&vnode->lock_work);
- vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
- afs_lock_may_be_available(vnode);
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_NEED_UNLOCK);
+ trace_afs_flock_ev(vnode, NULL, afs_flock_defer_unlock, 0);
+ queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0);
}
}
* whether we think that we have a locking permit.
*/
static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
- afs_lock_type_t type, bool can_sleep)
+ enum afs_flock_mode mode, afs_lock_type_t type)
{
afs_access_t access;
int ret;
if (type == AFS_LOCK_READ) {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK)))
return -EACCES;
- if (vnode->status.lock_count == -1 && !can_sleep)
- return -EAGAIN; /* Write locked */
} else {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE)))
return -EACCES;
- if (vnode->status.lock_count != 0 && !can_sleep)
- return -EAGAIN; /* Locked */
}
return 0;
}
- /*
- * Remove the front runner from the pending queue.
- * - The caller must hold vnode->lock.
- */
- static void afs_dequeue_lock(struct afs_vnode *vnode, struct file_lock *fl)
- {
- struct file_lock *next;
-
- _enter("");
-
- /* ->lock_type, ->lock_key and ->lock_state only belong to this
- * file_lock if we're at the front of the pending queue or if we have
- * the lock granted or if the lock_state is NEED_UNLOCK or UNLOCKING.
- */
- if (vnode->granted_locks.next == &fl->fl_u.afs.link &&
- vnode->granted_locks.prev == &fl->fl_u.afs.link) {
- list_del_init(&fl->fl_u.afs.link);
- afs_defer_unlock(vnode);
- return;
- }
-
- if (!list_empty(&vnode->granted_locks) ||
- vnode->pending_locks.next != &fl->fl_u.afs.link) {
- list_del_init(&fl->fl_u.afs.link);
- return;
- }
-
- list_del_init(&fl->fl_u.afs.link);
- key_put(vnode->lock_key);
- vnode->lock_key = NULL;
- vnode->lock_state = AFS_VNODE_LOCK_NONE;
-
- if (list_empty(&vnode->pending_locks))
- return;
-
- /* The new front of the queue now owns the state variables. */
- next = list_entry(vnode->pending_locks.next,
- struct file_lock, fl_u.afs.link);
- vnode->lock_key = key_get(afs_file_key(next->fl_file));
- vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
- vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
- afs_lock_may_be_available(vnode);
- }
-
/*
* request a lock on a file on the server
*/
{
struct inode *inode = locks_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode);
+ enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode;
afs_lock_type_t type;
struct key *key = afs_file_key(file);
+ bool partial, no_server_lock = false;
int ret;
- _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
+ if (mode == afs_flock_mode_unset)
+ mode = afs_flock_mode_openafs;
- /* only whole-file locks are supported */
- if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
- return -EINVAL;
+ _enter("{%llx:%llu},%llu-%llu,%u,%u",
+ vnode->fid.vid, vnode->fid.vnode,
+ fl->fl_start, fl->fl_end, fl->fl_type, mode);
fl->fl_ops = &afs_lock_ops;
INIT_LIST_HEAD(&fl->fl_u.afs.link);
fl->fl_u.afs.state = AFS_LOCK_PENDING;
+ partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX);
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
+ if (mode == afs_flock_mode_write && partial)
+ type = AFS_LOCK_WRITE;
- ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP);
+ ret = afs_do_setlk_check(vnode, key, mode, type);
if (ret < 0)
return ret;
- spin_lock(&vnode->lock);
+ trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock);
- /* If we've already got a readlock on the server then we instantly
- * grant another readlock, irrespective of whether there are any
- * pending writelocks.
+ /* AFS3 protocol only supports full-file locks and doesn't provide any
+ * method of upgrade/downgrade, so we need to emulate for partial-file
+ * locks.
+ *
+ * The OpenAFS client only gets a server lock for a full-file lock and
+ * keeps partial-file locks local. Allow this behaviour to be emulated
+ * (as the default).
*/
- if (type == AFS_LOCK_READ &&
- vnode->lock_state == AFS_VNODE_LOCK_GRANTED &&
- vnode->lock_type == AFS_LOCK_READ) {
- _debug("instant readlock");
- ASSERT(!list_empty(&vnode->granted_locks));
- goto share_existing_lock;
+ if (mode == afs_flock_mode_local ||
+ (partial && mode == afs_flock_mode_openafs)) {
+ no_server_lock = true;
+ goto skip_server_lock;
}
+ spin_lock(&vnode->lock);
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
+ ret = -ENOENT;
+ if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
+ goto error_unlock;
+
+ /* If we've already got a lock on the server then try to move to having
+ * the VFS grant the requested lock. Note that this means that other
+ * clients may get starved out.
+ */
+ _debug("try %u", vnode->lock_state);
+ if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED) {
+ if (type == AFS_LOCK_READ) {
+ _debug("instant readlock");
+ list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
+ fl->fl_u.afs.state = AFS_LOCK_GRANTED;
+ goto vnode_is_locked_u;
+ }
+
+ if (vnode->lock_type == AFS_LOCK_WRITE) {
+ _debug("instant writelock");
+ list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
+ fl->fl_u.afs.state = AFS_LOCK_GRANTED;
+ goto vnode_is_locked_u;
+ }
+ }
+
+ if (vnode->lock_state == AFS_VNODE_LOCK_NONE &&
+ !(fl->fl_flags & FL_SLEEP)) {
+ ret = -EAGAIN;
+ if (type == AFS_LOCK_READ) {
+ if (vnode->status.lock_count == -1)
+ goto lock_is_contended; /* Write locked */
+ } else {
+ if (vnode->status.lock_count != 0)
+ goto lock_is_contended; /* Locked */
+ }
+ }
+
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
goto need_to_wait;
+ try_to_lock:
/* We don't have a lock on this vnode and we aren't currently waiting
* for one either, so ask the server for a lock.
*
* Note that we need to be careful if we get interrupted by a signal
* after dispatching the request as we may still get the lock, even
* though we don't wait for the reply (it's not too bad a problem - the
- * lock will expire in 10 mins anyway).
+ * lock will expire in 5 mins anyway).
*/
- _debug("not locked");
+ trace_afs_flock_ev(vnode, fl, afs_flock_try_to_lock, 0);
vnode->lock_key = key_get(key);
vnode->lock_type = type;
- vnode->lock_state = AFS_VNODE_LOCK_SETTING;
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING);
spin_unlock(&vnode->lock);
ret = afs_set_lock(vnode, key, type); /* RPC */
spin_lock(&vnode->lock);
switch (ret) {
+ case -EKEYREJECTED:
+ case -EKEYEXPIRED:
+ case -EKEYREVOKED:
+ case -EPERM:
+ case -EACCES:
+ fl->fl_u.afs.state = ret;
+ trace_afs_flock_ev(vnode, fl, afs_flock_fail_perm, ret);
+ list_del_init(&fl->fl_u.afs.link);
+ afs_next_locker(vnode, ret);
+ goto error_unlock;
+
+ case -ENOENT:
+ fl->fl_u.afs.state = ret;
+ trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret);
+ list_del_init(&fl->fl_u.afs.link);
+ afs_kill_lockers_enoent(vnode);
+ goto error_unlock;
+
default:
- goto abort_attempt;
+ fl->fl_u.afs.state = ret;
+ trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret);
+ list_del_init(&fl->fl_u.afs.link);
+ afs_next_locker(vnode, 0);
+ goto error_unlock;
case -EWOULDBLOCK:
/* The server doesn't have a lock-waiting queue, so the client
* will have to retry. The server will break the outstanding
* callbacks on a file when a lock is released.
*/
- _debug("would block");
ASSERT(list_empty(&vnode->granted_locks));
ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link);
- vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
- goto need_to_wait;
+ goto lock_is_contended;
case 0:
- _debug("acquired");
- break;
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED);
+ trace_afs_flock_ev(vnode, fl, afs_flock_acquired, type);
+ afs_grant_locks(vnode);
+ goto vnode_is_locked_u;
}
- /* we've acquired a server lock, but it needs to be renewed after 5
- * mins */
- vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
- afs_schedule_lock_extension(vnode);
-
- share_existing_lock:
- /* the lock has been granted as far as we're concerned... */
- fl->fl_u.afs.state = AFS_LOCK_GRANTED;
- list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
-
- given_lock:
- /* ... but we do still need to get the VFS's blessing */
+ vnode_is_locked_u:
spin_unlock(&vnode->lock);
-
- ret = posix_lock_file(file, fl, NULL);
+ vnode_is_locked:
+ /* the lock has been granted by the server... */
+ ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED);
+
+ skip_server_lock:
+ /* ... but the VFS still needs to distribute access on this client. */
+ trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0);
+ ret = locks_lock_file_wait(file, fl);
+ trace_afs_flock_ev(vnode, fl, afs_flock_vfs_lock, ret);
if (ret < 0)
goto vfs_rejected_lock;
_leave(" = 0");
return 0;
+ lock_is_contended:
+ if (!(fl->fl_flags & FL_SLEEP)) {
+ list_del_init(&fl->fl_u.afs.link);
+ afs_next_locker(vnode, 0);
+ ret = -EAGAIN;
+ goto error_unlock;
+ }
+
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_WAITING_FOR_CB);
+ trace_afs_flock_ev(vnode, fl, afs_flock_would_block, ret);
+ queue_delayed_work(afs_lock_manager, &vnode->lock_work, HZ * 5);
+
need_to_wait:
/* We're going to have to wait. Either this client doesn't have a lock
* on the server yet and we need to wait for a callback to occur, or
- * the client does have a lock on the server, but it belongs to some
- * other process(es) and is incompatible with the lock we want.
+ * the client does have a lock on the server, but it's shared and we
+ * need an exclusive lock.
*/
- ret = -EAGAIN;
- if (fl->fl_flags & FL_SLEEP) {
- spin_unlock(&vnode->lock);
+ spin_unlock(&vnode->lock);
- _debug("sleep");
- ret = wait_event_interruptible(fl->fl_wait,
- fl->fl_u.afs.state != AFS_LOCK_PENDING);
+ trace_afs_flock_ev(vnode, fl, afs_flock_waiting, 0);
+ ret = wait_event_interruptible(fl->fl_wait,
+ fl->fl_u.afs.state != AFS_LOCK_PENDING);
+ trace_afs_flock_ev(vnode, fl, afs_flock_waited, ret);
+ if (fl->fl_u.afs.state >= 0 && fl->fl_u.afs.state != AFS_LOCK_GRANTED) {
spin_lock(&vnode->lock);
- }
- if (fl->fl_u.afs.state == AFS_LOCK_GRANTED)
- goto given_lock;
- if (fl->fl_u.afs.state < 0)
- ret = fl->fl_u.afs.state;
+ switch (fl->fl_u.afs.state) {
+ case AFS_LOCK_YOUR_TRY:
+ fl->fl_u.afs.state = AFS_LOCK_PENDING;
+ goto try_to_lock;
+ case AFS_LOCK_PENDING:
+ if (ret > 0) {
+ /* We need to retry the lock. We may not be
+ * notified by the server if it just expired
+ * rather than being released.
+ */
+ ASSERTCMP(vnode->lock_state, ==, AFS_VNODE_LOCK_WAITING_FOR_CB);
+ afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING);
+ fl->fl_u.afs.state = AFS_LOCK_PENDING;
+ goto try_to_lock;
+ }
+ goto error_unlock;
+ case AFS_LOCK_GRANTED:
+ default:
+ break;
+ }
- abort_attempt:
- /* we aren't going to get the lock, either because we're unwilling to
- * wait, or because some signal happened */
- _debug("abort");
- afs_dequeue_lock(vnode, fl);
+ spin_unlock(&vnode->lock);
+ }
- error_unlock:
- spin_unlock(&vnode->lock);
- _leave(" = %d", ret);
- return ret;
+ if (fl->fl_u.afs.state == AFS_LOCK_GRANTED)
+ goto vnode_is_locked;
+ ret = fl->fl_u.afs.state;
+ goto error;
vfs_rejected_lock:
/* The VFS rejected the lock we just obtained, so we have to discard
* deal with.
*/
_debug("vfs refused %d", ret);
+ if (no_server_lock)
+ goto error;
spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link);
- if (list_empty(&vnode->granted_locks))
- afs_defer_unlock(vnode);
- goto error_unlock;
+ afs_defer_unlock(vnode);
+
+ error_unlock:
+ spin_unlock(&vnode->lock);
+ error:
+ _leave(" = %d", ret);
+ return ret;
}
/*
_enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
+ trace_afs_flock_op(vnode, fl, afs_flock_op_unlock);
+
/* Flush all pending writes before doing anything with locks. */
vfs_fsync(file, 0);
- /* only whole-file unlocks are supported */
- if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
- return -EINVAL;
-
- ret = posix_lock_file(file, fl, NULL);
+ ret = locks_lock_file_wait(file, fl);
_leave(" = %d [%u]", ret, vnode->lock_state);
return ret;
}
_enter("");
+ if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
+ return -ENOENT;
+
fl->fl_type = F_UNLCK;
/* check local lock records first */
goto error;
lock_count = READ_ONCE(vnode->status.lock_count);
- if (lock_count > 0)
- fl->fl_type = F_RDLCK;
- else
- fl->fl_type = F_WRLCK;
- fl->fl_start = 0;
- fl->fl_end = OFFSET_MAX;
+ if (lock_count != 0) {
+ if (lock_count > 0)
+ fl->fl_type = F_RDLCK;
+ else
+ fl->fl_type = F_WRLCK;
+ fl->fl_start = 0;
+ fl->fl_end = OFFSET_MAX;
+ fl->fl_pid = 0;
+ }
}
ret = 0;
int afs_lock(struct file *file, int cmd, struct file_lock *fl)
{
struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
+ enum afs_flock_operation op;
+ int ret;
_enter("{%llx:%llu},%d,{t=%x,fl=%x,r=%Ld:%Ld}",
vnode->fid.vid, vnode->fid.vnode, cmd,
if (IS_GETLK(cmd))
return afs_do_getlk(file, fl);
+
+ fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id);
+ trace_afs_flock_op(vnode, fl, afs_flock_op_lock);
+
if (fl->fl_type == F_UNLCK)
- return afs_do_unlk(file, fl);
- return afs_do_setlk(file, fl);
+ ret = afs_do_unlk(file, fl);
+ else
+ ret = afs_do_setlk(file, fl);
+
+ switch (ret) {
+ case 0: op = afs_flock_op_return_ok; break;
+ case -EAGAIN: op = afs_flock_op_return_eagain; break;
+ case -EDEADLK: op = afs_flock_op_return_edeadlk; break;
+ default: op = afs_flock_op_return_error; break;
+ }
+ trace_afs_flock_op(vnode, fl, op);
+ return ret;
}
/*
int afs_flock(struct file *file, int cmd, struct file_lock *fl)
{
struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
+ enum afs_flock_operation op;
+ int ret;
_enter("{%llx:%llu},%d,{t=%x,fl=%x}",
vnode->fid.vid, vnode->fid.vnode, cmd,
if (!(fl->fl_flags & FL_FLOCK))
return -ENOLCK;
+ fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id);
+ trace_afs_flock_op(vnode, fl, afs_flock_op_flock);
+
/* we're simulating flock() locks using posix locks on the server */
if (fl->fl_type == F_UNLCK)
- return afs_do_unlk(file, fl);
- return afs_do_setlk(file, fl);
+ ret = afs_do_unlk(file, fl);
+ else
+ ret = afs_do_setlk(file, fl);
+
+ switch (ret) {
+ case 0: op = afs_flock_op_return_ok; break;
+ case -EAGAIN: op = afs_flock_op_return_eagain; break;
+ case -EDEADLK: op = afs_flock_op_return_edeadlk; break;
+ default: op = afs_flock_op_return_error; break;
+ }
+ trace_afs_flock_op(vnode, fl, op);
+ return ret;
}
/*
_enter("");
+ new->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id);
+
spin_lock(&vnode->lock);
+ trace_afs_flock_op(vnode, new, afs_flock_op_copy_lock);
list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link);
spin_unlock(&vnode->lock);
}
_enter("");
spin_lock(&vnode->lock);
- afs_dequeue_lock(vnode, fl);
+
+ trace_afs_flock_op(vnode, fl, afs_flock_op_release_lock);
+ list_del_init(&fl->fl_u.afs.link);
+ if (list_empty(&vnode->granted_locks))
+ afs_defer_unlock(vnode);
+
_debug("state %u for %p", vnode->lock_state, vnode);
spin_unlock(&vnode->lock);
}
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_extract_to_tmp(call);
}
- /* extract the returned data length */
+ /* Fall through - and extract the returned data length */
case 1:
_debug("extract data length");
ret = afs_extract_data(call, true);
iov_iter_bvec(&call->iter, READ, call->bvec, 1, size);
ASSERTCMP(size, <=, PAGE_SIZE);
- /* extract the returned data */
+ /* Fall through - and extract the returned data */
case 2:
_debug("extract data %zu/%llu",
iov_iter_count(&call->iter), req->remain);
/* Discard any excess data the server gave us */
iov_iter_discard(&call->iter, READ, req->actual_len - req->len);
call->unmarshall = 3;
+
+ /* Fall through */
case 3:
_debug("extract discard %zu/%llu",
iov_iter_count(&call->iter), req->actual_len - req->len);
call->unmarshall = 4;
afs_extract_to_buf(call, (21 + 3 + 6) * 4);
- /* extract the metadata */
+ /* Fall through - and extract the metadata */
case 4:
ret = afs_extract_data(call, false);
if (ret < 0)
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
*bp++ = 0; /* segment size */
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
- * deliver reply data to an FS.RemoveFile or FS.RemoveDir
+ * Deliver reply data to any operation that returns file status and volume
+ * sync.
*/
- static int afs_deliver_fs_remove(struct afs_call *call)
+ static int afs_deliver_fs_status_and_vol(struct afs_call *call)
{
struct afs_vnode *vnode = call->reply[0];
const __be32 *bp;
static const struct afs_call_type afs_RXFSRemoveFile = {
.name = "FS.RemoveFile",
.op = afs_FS_RemoveFile,
- .deliver = afs_deliver_fs_remove,
+ .deliver = afs_deliver_fs_status_and_vol,
.destructor = afs_flat_call_destructor,
};
static const struct afs_call_type afs_RXFSRemoveDir = {
.name = "FS.RemoveDir",
.op = afs_FS_RemoveDir,
- .deliver = afs_deliver_fs_remove,
+ .deliver = afs_deliver_fs_status_and_vol,
.destructor = afs_flat_call_destructor,
};
}
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &dvnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
*bp++ = htonl(vnode->fid.unique);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
*bp++ = 0; /* segment size */
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
}
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &orig_dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call2(call, &orig_dvnode->fid, orig_name, new_name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
*bp++ = htonl((u32) i_size);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->unmarshall++;
afs_extract_to_buf(call, 12 * 4);
- /* extract the returned status record */
+ /* Fall through - and extract the returned status record */
case 1:
_debug("extract status");
ret = afs_extract_data(call, true);
call->unmarshall++;
afs_extract_to_tmp(call);
- /* extract the volume name length */
+ /* Fall through - and extract the volume name length */
case 2:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the volume name */
+ /* Fall through - and extract the volume name */
case 3:
_debug("extract volname");
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
call->unmarshall++;
- /* extract the offline message length */
+ /* Fall through - and extract the offline message length */
case 4:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the offline message */
+ /* Fall through - and extract the offline message */
case 5:
_debug("extract offline");
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
call->unmarshall++;
- /* extract the message of the day length */
+ /* Fall through - and extract the message of the day length */
case 6:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the message of the day */
+ /* Fall through - and extract the message of the day */
case 7:
_debug("extract motd");
ret = afs_extract_data(call, false);
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
.name = "FS.SetLock",
.op = afs_FS_SetLock,
.deliver = afs_deliver_fs_xxxx_lock,
+ .done = afs_lock_op_done,
.destructor = afs_flat_call_destructor,
};
.name = "FS.ExtendLock",
.op = afs_FS_ExtendLock,
.deliver = afs_deliver_fs_xxxx_lock,
+ .done = afs_lock_op_done,
.destructor = afs_flat_call_destructor,
};
call->key = fc->key;
call->reply[0] = vnode;
+ call->want_reply_time = true;
/* marshall the parameters */
bp = call->request;
*bp++ = htonl(type);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_calli(call, &vnode->fid, type);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->key = fc->key;
call->reply[0] = vnode;
+ call->want_reply_time = true;
/* marshall the parameters */
bp = call->request;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
*bp++ = htonl(FSGIVEUPALLCALLBACKS);
/* Can't take a ref on server */
- return afs_make_call(ac, call, GFP_NOFS, false);
+ afs_make_call(ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, ac);
}
/*
afs_extract_to_tmp(call);
call->unmarshall++;
- /* Extract the capabilities word count */
+ /* Fall through - and extract the capabilities word count */
case 1:
ret = afs_extract_data(call, true);
if (ret < 0)
iov_iter_discard(&call->iter, READ, count * sizeof(__be32));
call->unmarshall++;
- /* Extract capabilities words */
+ /* Fall through - and extract capabilities words */
case 2:
ret = afs_extract_data(call, false);
if (ret < 0)
* Probe a fileserver for the capabilities that it supports. This can
* return up to 196 words.
*/
- int afs_fs_get_capabilities(struct afs_net *net,
- struct afs_server *server,
- struct afs_addr_cursor *ac,
- struct key *key,
- unsigned int server_index,
- bool async)
+ struct afs_call *afs_fs_get_capabilities(struct afs_net *net,
+ struct afs_server *server,
+ struct afs_addr_cursor *ac,
+ struct key *key,
+ unsigned int server_index)
{
struct afs_call *call;
__be32 *bp;
call = afs_alloc_flat_call(net, &afs_RXFSGetCapabilities, 1 * 4, 16 * 4);
if (!call)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
call->key = key;
call->reply[0] = afs_get_server(server);
call->reply[1] = (void *)(long)server_index;
call->upgrade = true;
call->want_reply_time = true;
+ call->async = true;
/* marshall the parameters */
bp = call->request;
/* Can't take a ref on server */
trace_afs_make_fs_call(call, NULL);
- return afs_make_call(ac, call, GFP_NOFS, async);
+ afs_make_call(ac, call, GFP_NOFS);
+ return call;
}
/*
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->unmarshall++;
/* Extract the file status count and array in two steps */
+ /* Fall through */
case 1:
_debug("extract status count");
ret = afs_extract_data(call, true);
more_counts:
afs_extract_to_buf(call, 21 * sizeof(__be32));
+ /* Fall through */
case 2:
_debug("extract status array %u", call->count);
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
/* Extract the callback count and array in two steps */
+ /* Fall through */
case 3:
_debug("extract CB count");
ret = afs_extract_data(call, true);
more_cbs:
afs_extract_to_buf(call, 3 * sizeof(__be32));
+ /* Fall through */
case 4:
_debug("extract CB array");
ret = afs_extract_data(call, true);
afs_extract_to_buf(call, 6 * sizeof(__be32));
call->unmarshall++;
+ /* Fall through */
case 5:
ret = afs_extract_data(call, false);
if (ret < 0)
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &fids[0]);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
+ }
+
+ /*
+ * deliver reply data to an FS.FetchACL
+ */
+ static int afs_deliver_fs_fetch_acl(struct afs_call *call)
+ {
+ struct afs_vnode *vnode = call->reply[1];
+ struct afs_acl *acl;
+ const __be32 *bp;
+ unsigned int size;
+ int ret;
+
+ _enter("{%u}", call->unmarshall);
+
+ switch (call->unmarshall) {
+ case 0:
+ afs_extract_to_tmp(call);
+ call->unmarshall++;
+
+ /* extract the returned data length */
+ case 1:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ size = call->count2 = ntohl(call->tmp);
+ size = round_up(size, 4);
+
+ acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
+ if (!acl)
+ return -ENOMEM;
+ call->reply[0] = acl;
+ acl->size = call->count2;
+ afs_extract_begin(call, acl->data, size);
+ call->unmarshall++;
+
+ /* extract the returned data */
+ case 2:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ afs_extract_to_buf(call, (21 + 6) * 4);
+ call->unmarshall++;
+
+ /* extract the metadata */
+ case 3:
+ ret = afs_extract_data(call, false);
+ if (ret < 0)
+ return ret;
+
+ bp = call->buffer;
+ ret = afs_decode_status(call, &bp, &vnode->status, vnode,
+ &vnode->status.data_version, NULL);
+ if (ret < 0)
+ return ret;
+ xdr_decode_AFSVolSync(&bp, call->reply[2]);
+
+ call->unmarshall++;
+
+ case 4:
+ break;
+ }
+
+ _leave(" = 0 [done]");
+ return 0;
+ }
+
+ static void afs_destroy_fs_fetch_acl(struct afs_call *call)
+ {
+ kfree(call->reply[0]);
+ afs_flat_call_destructor(call);
+ }
+
+ /*
+ * FS.FetchACL operation type
+ */
+ static const struct afs_call_type afs_RXFSFetchACL = {
+ .name = "FS.FetchACL",
+ .op = afs_FS_FetchACL,
+ .deliver = afs_deliver_fs_fetch_acl,
+ .destructor = afs_destroy_fs_fetch_acl,
+ };
+
+ /*
+ * Fetch the ACL for a file.
+ */
+ struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *fc)
+ {
+ struct afs_vnode *vnode = fc->vnode;
+ struct afs_call *call;
+ struct afs_net *net = afs_v2net(vnode);
+ __be32 *bp;
+
+ _enter(",%x,{%llx:%llu},,",
+ key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+ call = afs_alloc_flat_call(net, &afs_RXFSFetchACL, 16, (21 + 6) * 4);
+ if (!call) {
+ fc->ac.error = -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ call->key = fc->key;
+ call->reply[0] = NULL;
+ call->reply[1] = vnode;
+ call->reply[2] = NULL; /* volsync */
+ call->ret_reply0 = true;
+
+ /* marshall the parameters */
+ bp = call->request;
+ bp[0] = htonl(FSFETCHACL);
+ bp[1] = htonl(vnode->fid.vid);
+ bp[2] = htonl(vnode->fid.vnode);
+ bp[3] = htonl(vnode->fid.unique);
+
+ call->cb_break = fc->cb_break;
+ afs_use_fs_server(call, fc->cbi);
+ trace_afs_make_fs_call(call, &vnode->fid);
+ afs_make_call(&fc->ac, call, GFP_KERNEL);
+ return (struct afs_acl *)afs_wait_for_call_to_complete(call, &fc->ac);
+ }
+
+ /*
+ * FS.StoreACL operation type
+ */
+ static const struct afs_call_type afs_RXFSStoreACL = {
+ .name = "FS.StoreACL",
+ .op = afs_FS_StoreACL,
+ .deliver = afs_deliver_fs_status_and_vol,
+ .destructor = afs_flat_call_destructor,
+ };
+
+ /*
+ * Fetch the ACL for a file.
+ */
+ int afs_fs_store_acl(struct afs_fs_cursor *fc, const struct afs_acl *acl)
+ {
+ struct afs_vnode *vnode = fc->vnode;
+ struct afs_call *call;
+ struct afs_net *net = afs_v2net(vnode);
+ size_t size;
+ __be32 *bp;
+
+ _enter(",%x,{%llx:%llu},,",
+ key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+ size = round_up(acl->size, 4);
+ call = afs_alloc_flat_call(net, &afs_RXFSStoreACL,
+ 5 * 4 + size, (21 + 6) * 4);
+ if (!call) {
+ fc->ac.error = -ENOMEM;
+ return -ENOMEM;
+ }
+
+ call->key = fc->key;
+ call->reply[0] = vnode;
+ call->reply[2] = NULL; /* volsync */
+
+ /* marshall the parameters */
+ bp = call->request;
+ bp[0] = htonl(FSSTOREACL);
+ bp[1] = htonl(vnode->fid.vid);
+ bp[2] = htonl(vnode->fid.vnode);
+ bp[3] = htonl(vnode->fid.unique);
+ bp[4] = htonl(acl->size);
+ memcpy(&bp[5], acl->data, acl->size);
+ if (acl->size != size)
+ memset((void *)&bp[5] + acl->size, 0, size - acl->size);
+
+ trace_afs_make_fs_call(call, &vnode->fid);
+ afs_make_call(&fc->ac, call, GFP_KERNEL);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
struct workqueue_struct *afs_async_calls;
static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long);
- static long afs_wait_for_call_to_complete(struct afs_call *, struct afs_addr_cursor *);
static void afs_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_delete_async_call(struct work_struct *);
static void afs_process_async_call(struct work_struct *);
}
/*
- * initiate a call
+ * Initiate a call and synchronously queue up the parameters for dispatch. Any
+ * error is stored into the call struct, which the caller must check for.
*/
- long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
- gfp_t gfp, bool async)
+ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
{
struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index];
struct rxrpc_call *rxcall;
call, call->type->name, key_serial(call->key),
atomic_read(&call->net->nr_outstanding_calls));
- call->async = async;
call->addr_ix = ac->index;
call->alist = afs_get_addrlist(ac->alist);
rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
(unsigned long)call,
tx_total_len, gfp,
- (async ?
+ (call->async ?
afs_wake_up_async_call :
afs_wake_up_call_waiter),
call->upgrade,
/* Note that at this point, we may have received the reply or an abort
* - and an asynchronous call may already have completed.
+ *
+ * afs_wait_for_call_to_complete(call, ac)
+ * must be called to synchronously clean up.
*/
- if (call->async) {
- afs_put_call(call);
- return -EINPROGRESS;
- }
-
- return afs_wait_for_call_to_complete(call, ac);
+ return;
error_do_abort:
if (ret != -ECONNABORTED) {
ac->error = ret;
call->state = AFS_CALL_COMPLETE;
- afs_put_call(call);
_leave(" = %d", ret);
- return ret;
}
/*
}
/*
- * wait synchronously for a call to complete
+ * Wait synchronously for a call to complete and clean up the call struct.
*/
- static long afs_wait_for_call_to_complete(struct afs_call *call,
- struct afs_addr_cursor *ac)
+ long afs_wait_for_call_to_complete(struct afs_call *call,
+ struct afs_addr_cursor *ac)
{
signed long rtt2, timeout;
long ret;
_enter("");
+ ret = call->error;
+ if (ret < 0)
+ goto out;
+
rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall);
rtt2 = nsecs_to_jiffies64(rtt) * 2;
if (rtt2 < 2)
break;
}
+ out:
_debug("call complete");
afs_put_call(call);
_leave(" = %p", (void *)ret);
_debug("oom");
rxrpc_kernel_abort_call(net->socket, call->rxcall,
RX_USER_ABORT, -ENOMEM, "KOO");
+ /* Fall through */
default:
_leave(" [error]");
return;
static void afs_kill_super(struct super_block *sb);
static struct inode *afs_alloc_inode(struct super_block *sb);
static void afs_destroy_inode(struct inode *inode);
+static void afs_free_inode(struct inode *inode);
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf);
static int afs_show_devname(struct seq_file *m, struct dentry *root);
static int afs_show_options(struct seq_file *m, struct dentry *root);
.init_fs_context = afs_init_fs_context,
.parameters = &afs_fs_parameters,
.kill_sb = afs_kill_super,
- .fs_flags = 0,
+ .fs_flags = FS_RENAME_DOES_D_MOVE,
};
MODULE_ALIAS_FS("afs");
.alloc_inode = afs_alloc_inode,
.drop_inode = afs_drop_inode,
.destroy_inode = afs_destroy_inode,
+ .free_inode = afs_free_inode,
.evict_inode = afs_evict_inode,
.show_devname = afs_show_devname,
.show_options = afs_show_options,
enum afs_param {
Opt_autocell,
Opt_dyn,
+ Opt_flock,
Opt_source,
};
static const struct fs_parameter_spec afs_param_specs[] = {
fsparam_flag ("autocell", Opt_autocell),
fsparam_flag ("dyn", Opt_dyn),
+ fsparam_enum ("flock", Opt_flock),
fsparam_string("source", Opt_source),
{}
};
+ static const struct fs_parameter_enum afs_param_enums[] = {
+ { Opt_flock, "local", afs_flock_mode_local },
+ { Opt_flock, "openafs", afs_flock_mode_openafs },
+ { Opt_flock, "strict", afs_flock_mode_strict },
+ { Opt_flock, "write", afs_flock_mode_write },
+ {}
+ };
+
static const struct fs_parameter_description afs_fs_parameters = {
.name = "kAFS",
.specs = afs_param_specs,
+ .enums = afs_param_enums,
};
/*
static int afs_show_options(struct seq_file *m, struct dentry *root)
{
struct afs_super_info *as = AFS_FS_S(root->d_sb);
+ const char *p = NULL;
if (as->dyn_root)
seq_puts(m, ",dyn");
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
seq_puts(m, ",autocell");
+ switch (as->flock_mode) {
+ case afs_flock_mode_unset: break;
+ case afs_flock_mode_local: p = "local"; break;
+ case afs_flock_mode_openafs: p = "openafs"; break;
+ case afs_flock_mode_strict: p = "strict"; break;
+ case afs_flock_mode_write: p = "write"; break;
+ }
+ if (p)
+ seq_printf(m, ",flock=%s", p);
+
return 0;
}
ctx->dyn_root = true;
break;
+ case Opt_flock:
+ ctx->flock_mode = result.uint_32;
+ break;
+
default:
return -EINVAL;
}
fid.vnode = 1;
fid.vnode_hi = 0;
fid.unique = 1;
- inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL);
+ inode = afs_iget(sb, ctx->key, &fid, NULL, NULL, NULL, NULL);
}
if (IS_ERR(inode))
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) {
as->net_ns = get_net(fc->net_ns);
+ as->flock_mode = ctx->flock_mode;
if (ctx->dyn_root) {
as->dyn_root = true;
} else {
}
fc->root = dget(sb->s_root);
+ trace_afs_get_tree(as->cell, as->volume);
_leave(" = 0 [%p]", sb);
return 0;
vnode->cb_type = 0;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
+ init_rwsem(&vnode->rmdir_lock);
+
_leave(" = %p", &vnode->vfs_inode);
return &vnode->vfs_inode;
}
-static void afs_i_callback(struct rcu_head *head)
+static void afs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct afs_vnode *vnode = AFS_FS_I(inode);
- kmem_cache_free(afs_inode_cachep, vnode);
+ kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode));
}
/*
ASSERTCMP(vnode->cb_interest, ==, NULL);
- call_rcu(&inode->i_rcu, afs_i_callback);
atomic_dec(&afs_count_active_inodes);
}
memset((void *)bp + volnamesz, 0, padsz);
trace_afs_make_vl_call(call);
- return (struct afs_vldb_entry *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
+ afs_make_call(&vc->ac, call, GFP_KERNEL);
+ return (struct afs_vldb_entry *)afs_wait_for_call_to_complete(call, &vc->ac);
}
/*
sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32));
call->unmarshall++;
- /* Extract the returned uuid, uniquifier, nentries and blkaddrs size */
+ /* Extract the returned uuid, uniquifier, nentries and
+ * blkaddrs size */
+ /* Fall through */
case 1:
ret = afs_extract_data(call, true);
if (ret < 0)
count = min(call->count, 4U);
afs_extract_to_buf(call, count * sizeof(__be32));
- /* Extract entries */
+ /* Fall through - and extract entries */
case 2:
ret = afs_extract_data(call, call->count > 4);
if (ret < 0)
r->uuid.node[i] = htonl(u->node[i]);
trace_afs_make_vl_call(call);
- return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
+ afs_make_call(&vc->ac, call, GFP_KERNEL);
+ return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
}
/*
afs_extract_to_tmp(call);
call->unmarshall++;
- /* Extract the capabilities word count */
+ /* Fall through - and extract the capabilities word count */
case 1:
ret = afs_extract_data(call, true);
if (ret < 0)
call->unmarshall++;
afs_extract_discard(call, count * sizeof(__be32));
- /* Extract capabilities words */
+ /* Fall through - and extract capabilities words */
case 2:
ret = afs_extract_data(call, false);
if (ret < 0)
* We use this to probe for service upgrade to determine what the server at the
* other end supports.
*/
- int afs_vl_get_capabilities(struct afs_net *net,
- struct afs_addr_cursor *ac,
- struct key *key,
- struct afs_vlserver *server,
- unsigned int server_index,
- bool async)
+ struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
+ struct afs_addr_cursor *ac,
+ struct key *key,
+ struct afs_vlserver *server,
+ unsigned int server_index)
{
struct afs_call *call;
__be32 *bp;
call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4);
if (!call)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
call->key = key;
call->reply[0] = afs_get_vlserver(server);
call->reply[1] = (void *)(long)server_index;
call->upgrade = true;
call->want_reply_time = true;
+ call->async = true;
/* marshall the parameters */
bp = call->request;
/* Can't take a ref on server */
trace_afs_make_vl_call(call);
- return afs_make_call(ac, call, GFP_KERNEL, async);
+ afs_make_call(ac, call, GFP_KERNEL);
+ return call;
}
/*
/* Extract the returned uuid, uniquifier, fsEndpoints count and
* either the first fsEndpoint type or the volEndpoints
* count if there are no fsEndpoints. */
+ /* Fall through */
case 1:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_to_buf(call, size);
call->unmarshall = 2;
- /* Extract fsEndpoints[] entries */
+ /* Fall through - and extract fsEndpoints[] entries */
case 2:
ret = afs_extract_data(call, true);
if (ret < 0)
* extract the type of the next endpoint when we extract the
* data of the current one, but this is the first...
*/
+ /* Fall through */
case 3:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_to_buf(call, size);
call->unmarshall = 4;
- /* Extract volEndpoints[] entries */
+ /* Fall through - and extract volEndpoints[] entries */
case 4:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_discard(call, 0);
call->unmarshall = 5;
- /* Done */
+ /* Fall through - Done */
case 5:
ret = afs_extract_data(call, false);
if (ret < 0)
memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */
trace_afs_make_vl_call(call);
- return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false);
+ afs_make_call(&vc->ac, call, GFP_KERNEL);
+ return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
}
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_extract_to_tmp64(call);
call->unmarshall++;
- /* extract the returned data length */
+ /* Fall through - and extract the returned data length */
case 1:
_debug("extract data length");
ret = afs_extract_data(call, true);
iov_iter_bvec(&call->iter, READ, call->bvec, 1, size);
ASSERTCMP(size, <=, PAGE_SIZE);
- /* extract the returned data */
+ /* Fall through - and extract the returned data */
case 2:
_debug("extract data %zu/%llu",
iov_iter_count(&call->iter), req->remain);
/* Discard any excess data the server gave us */
iov_iter_discard(&call->iter, READ, req->actual_len - req->len);
call->unmarshall = 3;
+
+ /* Fall through */
case 3:
_debug("extract discard %zu/%llu",
iov_iter_count(&call->iter), req->actual_len - req->len);
sizeof(struct yfs_xdr_YFSCallBack) +
sizeof(struct yfs_xdr_YFSVolSync));
- /* extract the metadata */
+ /* Fall through - and extract the metadata */
case 4:
ret = afs_extract_data(call, false);
if (ret < 0)
call->unmarshall++;
+ /* Fall through */
case 5:
break;
}
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
static const struct afs_call_type yfs_RXFSMakeDir = {
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &dvnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &dvnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &vnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call1(call, &dvnode->fid, name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &orig_dvnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_call2(call, &orig_dvnode->fid, orig_name, new_name);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->unmarshall++;
afs_extract_to_buf(call, sizeof(struct yfs_xdr_YFSFetchVolumeStatus));
- /* extract the returned status record */
+ /* Fall through - and extract the returned status record */
case 1:
_debug("extract status");
ret = afs_extract_data(call, true);
call->unmarshall++;
afs_extract_to_tmp(call);
- /* extract the volume name length */
+ /* Fall through - and extract the volume name length */
case 2:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the volume name */
+ /* Fall through - and extract the volume name */
case 3:
_debug("extract volname");
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
call->unmarshall++;
- /* extract the offline message length */
+ /* Fall through - and extract the offline message length */
case 4:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the offline message */
+ /* Fall through - and extract the offline message */
case 5:
_debug("extract offline");
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
call->unmarshall++;
- /* extract the message of the day length */
+ /* Fall through - and extract the message of the day length */
case 6:
ret = afs_extract_data(call, true);
if (ret < 0)
afs_extract_begin(call, call->reply[2], size);
call->unmarshall++;
- /* extract the message of the day */
+ /* Fall through - and extract the message of the day */
case 7:
_debug("extract motd");
ret = afs_extract_data(call, false);
call->unmarshall++;
+ /* Fall through */
case 8:
break;
}
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
- * Deliver reply data to an YFS.SetLock, YFS.ExtendLock or YFS.ReleaseLock
+ * Deliver reply data to operations that just return a file status and a volume
+ * sync record.
*/
- static int yfs_deliver_fs_xxxx_lock(struct afs_call *call)
+ static int yfs_deliver_status_and_volsync(struct afs_call *call)
{
struct afs_vnode *vnode = call->reply[0];
const __be32 *bp;
static const struct afs_call_type yfs_RXYFSSetLock = {
.name = "YFS.SetLock",
.op = yfs_FS_SetLock,
- .deliver = yfs_deliver_fs_xxxx_lock,
+ .deliver = yfs_deliver_status_and_volsync,
+ .done = afs_lock_op_done,
.destructor = afs_flat_call_destructor,
};
static const struct afs_call_type yfs_RXYFSExtendLock = {
.name = "YFS.ExtendLock",
.op = yfs_FS_ExtendLock,
- .deliver = yfs_deliver_fs_xxxx_lock,
+ .deliver = yfs_deliver_status_and_volsync,
+ .done = afs_lock_op_done,
.destructor = afs_flat_call_destructor,
};
static const struct afs_call_type yfs_RXYFSReleaseLock = {
.name = "YFS.ReleaseLock",
.op = yfs_FS_ReleaseLock,
- .deliver = yfs_deliver_fs_xxxx_lock,
+ .deliver = yfs_deliver_status_and_volsync,
.destructor = afs_flat_call_destructor,
};
call->key = fc->key;
call->reply[0] = vnode;
+ call->want_reply_time = true;
/* marshall the parameters */
bp = call->request;
yfs_check_req(call, bp);
afs_use_fs_server(call, fc->cbi);
- trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ trace_afs_make_fs_calli(call, &vnode->fid, type);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->key = fc->key;
call->reply[0] = vnode;
+ call->want_reply_time = true;
/* marshall the parameters */
bp = call->request;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, fid);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
/*
call->unmarshall++;
/* Extract the file status count and array in two steps */
+ /* Fall through */
case 1:
_debug("extract status count");
ret = afs_extract_data(call, true);
more_counts:
afs_extract_to_buf(call, sizeof(struct yfs_xdr_YFSFetchStatus));
+ /* Fall through */
case 2:
_debug("extract status array %u", call->count);
ret = afs_extract_data(call, true);
afs_extract_to_tmp(call);
/* Extract the callback count and array in two steps */
+ /* Fall through */
case 3:
_debug("extract CB count");
ret = afs_extract_data(call, true);
more_cbs:
afs_extract_to_buf(call, sizeof(struct yfs_xdr_YFSCallBack));
+ /* Fall through */
case 4:
_debug("extract CB array");
ret = afs_extract_data(call, true);
afs_extract_to_buf(call, sizeof(struct yfs_xdr_YFSVolSync));
call->unmarshall++;
+ /* Fall through */
case 5:
ret = afs_extract_data(call, false);
if (ret < 0)
call->unmarshall++;
+ /* Fall through */
case 6:
break;
}
call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &fids[0]);
- return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+ afs_make_call(&fc->ac, call, GFP_NOFS);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
+ }
+
+ /*
+ * Deliver reply data to an YFS.FetchOpaqueACL.
+ */
+ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call)
+ {
+ struct afs_volsync *volsync = call->reply[2];
+ struct afs_vnode *vnode = call->reply[1];
+ struct yfs_acl *yacl = call->reply[0];
+ struct afs_acl *acl;
+ const __be32 *bp;
+ unsigned int size;
+ int ret;
+
+ _enter("{%u}", call->unmarshall);
+
+ switch (call->unmarshall) {
+ case 0:
+ afs_extract_to_tmp(call);
+ call->unmarshall++;
+
+ /* Extract the file ACL length */
+ case 1:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ size = call->count2 = ntohl(call->tmp);
+ size = round_up(size, 4);
+
+ if (yacl->flags & YFS_ACL_WANT_ACL) {
+ acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
+ if (!acl)
+ return -ENOMEM;
+ yacl->acl = acl;
+ acl->size = call->count2;
+ afs_extract_begin(call, acl->data, size);
+ } else {
+ iov_iter_discard(&call->iter, READ, size);
+ }
+ call->unmarshall++;
+
+ /* Extract the file ACL */
+ case 2:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ afs_extract_to_tmp(call);
+ call->unmarshall++;
+
+ /* Extract the volume ACL length */
+ case 3:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ size = call->count2 = ntohl(call->tmp);
+ size = round_up(size, 4);
+
+ if (yacl->flags & YFS_ACL_WANT_VOL_ACL) {
+ acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
+ if (!acl)
+ return -ENOMEM;
+ yacl->vol_acl = acl;
+ acl->size = call->count2;
+ afs_extract_begin(call, acl->data, size);
+ } else {
+ iov_iter_discard(&call->iter, READ, size);
+ }
+ call->unmarshall++;
+
+ /* Extract the volume ACL */
+ case 4:
+ ret = afs_extract_data(call, true);
+ if (ret < 0)
+ return ret;
+
+ afs_extract_to_buf(call,
+ sizeof(__be32) * 2 +
+ sizeof(struct yfs_xdr_YFSFetchStatus) +
+ sizeof(struct yfs_xdr_YFSVolSync));
+ call->unmarshall++;
+
+ /* extract the metadata */
+ case 5:
+ ret = afs_extract_data(call, false);
+ if (ret < 0)
+ return ret;
+
+ bp = call->buffer;
+ yacl->inherit_flag = ntohl(*bp++);
+ yacl->num_cleaned = ntohl(*bp++);
+ ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
+ &call->expected_version, NULL);
+ if (ret < 0)
+ return ret;
+ xdr_decode_YFSVolSync(&bp, volsync);
+
+ call->unmarshall++;
+
+ case 6:
+ break;
+ }
+
+ _leave(" = 0 [done]");
+ return 0;
+ }
+
+ void yfs_free_opaque_acl(struct yfs_acl *yacl)
+ {
+ if (yacl) {
+ kfree(yacl->acl);
+ kfree(yacl->vol_acl);
+ kfree(yacl);
+ }
+ }
+
+ static void yfs_destroy_fs_fetch_opaque_acl(struct afs_call *call)
+ {
+ yfs_free_opaque_acl(call->reply[0]);
+ afs_flat_call_destructor(call);
+ }
+
+ /*
+ * YFS.FetchOpaqueACL operation type
+ */
+ static const struct afs_call_type yfs_RXYFSFetchOpaqueACL = {
+ .name = "YFS.FetchOpaqueACL",
+ .op = yfs_FS_FetchOpaqueACL,
+ .deliver = yfs_deliver_fs_fetch_opaque_acl,
+ .destructor = yfs_destroy_fs_fetch_opaque_acl,
+ };
+
+ /*
+ * Fetch the YFS advanced ACLs for a file.
+ */
+ struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc,
+ unsigned int flags)
+ {
+ struct afs_vnode *vnode = fc->vnode;
+ struct afs_call *call;
+ struct yfs_acl *yacl;
+ struct afs_net *net = afs_v2net(vnode);
+ __be32 *bp;
+
+ _enter(",%x,{%llx:%llu},,",
+ key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+ call = afs_alloc_flat_call(net, &yfs_RXYFSFetchOpaqueACL,
+ sizeof(__be32) * 2 +
+ sizeof(struct yfs_xdr_YFSFid),
+ sizeof(__be32) * 2 +
+ sizeof(struct yfs_xdr_YFSFetchStatus) +
+ sizeof(struct yfs_xdr_YFSVolSync));
+ if (!call)
+ goto nomem;
+
+ yacl = kzalloc(sizeof(struct yfs_acl), GFP_KERNEL);
+ if (!yacl)
+ goto nomem_call;
+
+ yacl->flags = flags;
+ call->key = fc->key;
+ call->reply[0] = yacl;
+ call->reply[1] = vnode;
+ call->reply[2] = NULL; /* volsync */
+ call->ret_reply0 = true;
+
+ /* marshall the parameters */
+ bp = call->request;
+ bp = xdr_encode_u32(bp, YFSFETCHOPAQUEACL);
+ bp = xdr_encode_u32(bp, 0); /* RPC flags */
+ bp = xdr_encode_YFSFid(bp, &vnode->fid);
+ yfs_check_req(call, bp);
+
+ call->cb_break = fc->cb_break;
+ afs_use_fs_server(call, fc->cbi);
+ trace_afs_make_fs_call(call, &vnode->fid);
+ afs_make_call(&fc->ac, call, GFP_KERNEL);
+ return (struct yfs_acl *)afs_wait_for_call_to_complete(call, &fc->ac);
+
+ nomem_call:
+ afs_put_call(call);
+ nomem:
+ fc->ac.error = -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * YFS.StoreOpaqueACL2 operation type
+ */
+ static const struct afs_call_type yfs_RXYFSStoreOpaqueACL2 = {
+ .name = "YFS.StoreOpaqueACL2",
+ .op = yfs_FS_StoreOpaqueACL2,
+ .deliver = yfs_deliver_status_and_volsync,
+ .destructor = afs_flat_call_destructor,
+ };
+
+ /*
+ * Fetch the YFS ACL for a file.
+ */
+ int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *fc, const struct afs_acl *acl)
+ {
+ struct afs_vnode *vnode = fc->vnode;
+ struct afs_call *call;
+ struct afs_net *net = afs_v2net(vnode);
+ size_t size;
+ __be32 *bp;
+
+ _enter(",%x,{%llx:%llu},,",
+ key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+ size = round_up(acl->size, 4);
+ call = afs_alloc_flat_call(net, &yfs_RXYFSStoreStatus,
+ sizeof(__be32) * 2 +
+ sizeof(struct yfs_xdr_YFSFid) +
+ sizeof(__be32) + size,
+ sizeof(struct yfs_xdr_YFSFetchStatus) +
+ sizeof(struct yfs_xdr_YFSVolSync));
+ if (!call) {
+ fc->ac.error = -ENOMEM;
+ return -ENOMEM;
+ }
+
+ call->key = fc->key;
+ call->reply[0] = vnode;
+ call->reply[2] = NULL; /* volsync */
+
+ /* marshall the parameters */
+ bp = call->request;
+ bp = xdr_encode_u32(bp, YFSSTOREOPAQUEACL2);
+ bp = xdr_encode_u32(bp, 0); /* RPC flags */
+ bp = xdr_encode_YFSFid(bp, &vnode->fid);
+ bp = xdr_encode_u32(bp, acl->size);
+ memcpy(bp, acl->data, acl->size);
+ if (acl->size != size)
+ memset((void *)bp + acl->size, 0, size - acl->size);
+ yfs_check_req(call, bp);
+
+ trace_afs_make_fs_call(call, &vnode->fid);
+ afs_make_call(&fc->ac, call, GFP_KERNEL);
+ return afs_wait_for_call_to_complete(call, &fc->ac);
}
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
/* File is capable of returning -EAGAIN if I/O will block */
-#define FMODE_NOWAIT ((__force fmode_t)0x8000000)
+#define FMODE_NOWAIT ((__force fmode_t)0x8000000)
+
+/* File represents mount that needs unmounting */
+#define FMODE_NEED_UNMOUNT ((__force fmode_t)0x10000000)
/* File does not contribute to nr_files count */
-#define FMODE_NOACCOUNT ((__force fmode_t)0x20000000)
+#define FMODE_NOACCOUNT ((__force fmode_t)0x20000000)
/*
* Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
- const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ union {
+ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ void (*free_inode)(struct inode *);
+ };
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;
#define get_file_rcu_many(x, cnt) \
atomic_long_add_unless(&(x)->f_count, (cnt), 0)
#define get_file_rcu(x) get_file_rcu_many((x), 1)
-#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
#define file_count(x) atomic_long_read(&(x)->f_count)
#define MAX_NON_LFS ((1UL<<31) - 1)
struct {
struct list_head link; /* link in AFS vnode's pending_locks list */
int state; /* state of grant or error if -ve */
+ unsigned int debug_id;
} afs;
} fl_u;
} __randomize_layout;
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
+ void (*free_inode)(struct inode *);
void (*dirty_inode) (struct inode *, int flags);
int (*write_inode) (struct inode *, struct writeback_control *wbc);
int datasync);
extern int vfs_fsync(struct file *file, int datasync);
+extern int sync_file_range(struct file *file, loff_t offset, loff_t nbytes,
+ unsigned int flags);
+
/*
* Sync the bytes written if this was a synchronous write. Expect ki_pos
* to already be updated for the write, and will return either the amount