Commit a3a80255 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull AFS fixes from David Howells:
 "Here's a set of fixes for AFS:

   - Use struct_size() for kzalloc() size calculation.

   - When calling YFS.CreateFile rather than AFS.CreateFile, it is
     possible to create a file with a file lock already held. The
     default value indicating no lock required is actually -1, not 0.

   - Fix an oops in inode/vnode validation if the target inode doesn't
     have a server interest assigned (ie. a server that will notify us
     of changes by third parties).

   - Fix refcounting of keys in file locking.

   - Fix a race in refcounting asynchronous operations in the event of
     an error during request transmission. The provision of a dedicated
     function to get an extra ref on a call is split into a separate
     commit"

* tag 'afs-fixes-20190117' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix race in async call refcounting
  afs: Provide a function to get a ref on a call
  afs: Fix key refcounting in file locking code
  afs: Don't set vnode->cb_s_break in afs_validate()
  afs: Set correct lock type for the yfs CreateFile
  afs: Use struct_size() in kzalloc()
parents 6d060fa3 34fa4761
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ again:
		/* 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 = afs_file_key(next->fl_file);
		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;
@@ -413,7 +413,7 @@ static void afs_dequeue_lock(struct afs_vnode *vnode, struct file_lock *fl)
	/* 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 = afs_file_key(next->fl_file);
	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);
+2 −1
Original line number Diff line number Diff line
@@ -414,7 +414,6 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
	} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
		valid = true;
	} else {
		vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
		vnode->cb_v_break = vnode->volume->cb_v_break;
		valid = false;
	}
@@ -546,6 +545,8 @@ void afs_evict_inode(struct inode *inode)
#endif

	afs_put_permits(rcu_access_pointer(vnode->permit_cache));
	key_put(vnode->lock_key);
	vnode->lock_key = NULL;
	_leave("");
}

+11 −0
Original line number Diff line number Diff line
@@ -161,3 +161,14 @@ struct yfs_xdr_YFSStoreVolumeStatus {
	struct yfs_xdr_u64	max_quota;
	struct yfs_xdr_u64	file_quota;
} __packed;

enum yfs_lock_type {
	yfs_LockNone		= -1,
	yfs_LockRead		= 0,
	yfs_LockWrite		= 1,
	yfs_LockExtend		= 2,
	yfs_LockRelease		= 3,
	yfs_LockMandatoryRead	= 0x100,
	yfs_LockMandatoryWrite	= 0x101,
	yfs_LockMandatoryExtend	= 0x102,
};
+42 −11
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ 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 *);
static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long);
@@ -203,20 +204,26 @@ void afs_put_call(struct afs_call *call)
	}
}

/*
 * Queue the call for actual work.
 */
static void afs_queue_call_work(struct afs_call *call)
static struct afs_call *afs_get_call(struct afs_call *call,
				     enum afs_call_trace why)
{
	if (call->type->work) {
	int u = atomic_inc_return(&call->usage);

		trace_afs_call(call, afs_call_trace_work, u,
	trace_afs_call(call, why, u,
		       atomic_read(&call->net->nr_outstanding_calls),
		       __builtin_return_address(0));
	return call;
}

/*
 * Queue the call for actual work.
 */
static void afs_queue_call_work(struct afs_call *call)
{
	if (call->type->work) {
		INIT_WORK(&call->work, call->type->work);

		afs_get_call(call, afs_call_trace_work);
		if (!queue_work(afs_wq, &call->work))
			afs_put_call(call);
	}
@@ -398,6 +405,12 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
		}
	}

	/* If the call is going to be asynchronous, we need an extra ref for
	 * the call to hold itself so the caller need not hang on to its ref.
	 */
	if (call->async)
		afs_get_call(call, afs_call_trace_get);

	/* create a call */
	rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
					 (unsigned long)call,
@@ -438,15 +451,17 @@ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
			goto error_do_abort;
	}

	/* at this point, an async call may no longer exist as it may have
	 * already completed */
	if (call->async)
	/* Note that at this point, we may have received the reply or an abort
	 * - and an asynchronous call may already have completed.
	 */
	if (call->async) {
		afs_put_call(call);
		return -EINPROGRESS;
	}

	return afs_wait_for_call_to_complete(call, ac);

error_do_abort:
	call->state = AFS_CALL_COMPLETE;
	if (ret != -ECONNABORTED) {
		rxrpc_kernel_abort_call(call->net->socket, rxcall,
					RX_USER_ABORT, ret, "KSD");
@@ -463,8 +478,24 @@ error_do_abort:
error_kill_call:
	if (call->type->done)
		call->type->done(call);

	/* We need to dispose of the extra ref we grabbed for an async call.
	 * The call, however, might be queued on afs_async_calls and we need to
	 * make sure we don't get any more notifications that might requeue it.
	 */
	if (call->rxcall) {
		rxrpc_kernel_end_call(call->net->socket, call->rxcall);
		call->rxcall = NULL;
	}
	if (call->async) {
		if (cancel_work_sync(&call->async_work))
			afs_put_call(call);
		afs_put_call(call);
	}

	ac->error = ret;
	call->state = AFS_CALL_COMPLETE;
	afs_put_call(call);
	_leave(" = %d", ret);
	return ret;
}
+1 −3
Original line number Diff line number Diff line
@@ -42,9 +42,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
		if (vldb->fs_mask[i] & type_mask)
			nr_servers++;

	slist = kzalloc(sizeof(struct afs_server_list) +
			sizeof(struct afs_server_entry) * nr_servers,
			GFP_KERNEL);
	slist = kzalloc(struct_size(slist, servers, nr_servers), GFP_KERNEL);
	if (!slist)
		goto error;

Loading