Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21b9f1c7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull AFS fixes from David Howells:
 "Here's a set of patches that fix a number of bugs in the in-kernel AFS
  client, including:

   - Fix directory locking to not use individual page locks for
     directory reading/scanning but rather to use a semaphore on the
     afs_vnode struct as the directory contents must be read in a single
     blob and data from different reads must not be mixed as the entire
     contents may be shuffled about between reads.

   - Fix address list parsing to handle port specifiers correctly.

   - Only give up callback records on a server if we actually talked to
     that server (we might not be able to access a server).

   - Fix some callback handling bugs, including refcounting,
     whole-volume callbacks and when callbacks actually get broken in
     response to a CB.CallBack op.

   - Fix some server/address rotation bugs, including giving up if we
     can't probe a server; giving up if a server says it doesn't have a
     volume, but there are more servers to try.

   - Fix the decoding of fetched statuses to be OpenAFS compatible.

   - Fix the handling of server lookups in Cache Manager ops (such as
     CB.InitCallBackState3) to use a UUID if possible and to handle no
     server being found.

   - Fix a bug in server lookup where not all addresses are compared.

   - Fix the non-encryption of calls that prevents some servers from
     being accessed (this also requires an AF_RXRPC patch that has
     already gone in through the net tree).

  There's also a patch that adds tracepoints to log Cache Manager ops
  that don't find a matching server, either by UUID or by address"

* tag 'afs-fixes-20180514' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix the non-encryption of calls
  afs: Fix CB.CallBack handling
  afs: Fix whole-volume callback handling
  afs: Fix afs_find_server search loop
  afs: Fix the handling of an unfound server in CM operations
  afs: Add a tracepoint to record callbacks from unlisted servers
  afs: Fix the handling of CB.InitCallBackState3 to find the server by UUID
  afs: Fix VNOVOL handling in address rotation
  afs: Fix AFSFetchStatus decoder to provide OpenAFS compatibility
  afs: Fix server rotation's handling of fileserver probe failure
  afs: Fix refcounting in callback registration
  afs: Fix giving up callbacks on server destruction
  afs: Fix address list parsing
  afs: Fix directory page locking
parents eeba2dfa 4776cab4
Loading
Loading
Loading
Loading
+15 −10
Original line number Diff line number Diff line
@@ -121,7 +121,7 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
	p = text;
	do {
		struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
		char tdelim = delim;
		const char *q, *stop;

		if (*p == delim) {
			p++;
@@ -130,28 +130,33 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,

		if (*p == '[') {
			p++;
			tdelim = ']';
			q = memchr(p, ']', end - p);
		} else {
			for (q = p; q < end; q++)
				if (*q == '+' || *q == delim)
					break;
		}

		if (in4_pton(p, end - p,
		if (in4_pton(p, q - p,
			     (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
			     tdelim, &p)) {
			     -1, &stop)) {
			srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
			srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
			srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
		} else if (in6_pton(p, end - p,
		} else if (in6_pton(p, q - p,
				    srx->transport.sin6.sin6_addr.s6_addr,
				    tdelim, &p)) {
				    -1, &stop)) {
			/* Nothing to do */
		} else {
			goto bad_address;
		}

		if (tdelim == ']') {
			if (p == end || *p != ']')
		if (stop != q)
			goto bad_address;

		p = q;
		if (q < end && *q == ']')
			p++;
		}

		if (p < end) {
			if (*p == '+') {
+61 −23
Original line number Diff line number Diff line
@@ -23,36 +23,55 @@
/*
 * Set up an interest-in-callbacks record for a volume on a server and
 * register it with the server.
 * - Called with volume->server_sem held.
 * - Called with vnode->io_lock held.
 */
int afs_register_server_cb_interest(struct afs_vnode *vnode,
				    struct afs_server_entry *entry)
				    struct afs_server_list *slist,
				    unsigned int index)
{
	struct afs_cb_interest *cbi = entry->cb_interest, *vcbi, *new, *x;
	struct afs_server_entry *entry = &slist->servers[index];
	struct afs_cb_interest *cbi, *vcbi, *new, *old;
	struct afs_server *server = entry->server;

again:
	if (vnode->cb_interest &&
	    likely(vnode->cb_interest == entry->cb_interest))
		return 0;

	read_lock(&slist->lock);
	cbi = afs_get_cb_interest(entry->cb_interest);
	read_unlock(&slist->lock);

	vcbi = vnode->cb_interest;
	if (vcbi) {
		if (vcbi == cbi)
		if (vcbi == cbi) {
			afs_put_cb_interest(afs_v2net(vnode), cbi);
			return 0;
		}

		/* Use a new interest in the server list for the same server
		 * rather than an old one that's still attached to a vnode.
		 */
		if (cbi && vcbi->server == cbi->server) {
			write_seqlock(&vnode->cb_lock);
			vnode->cb_interest = afs_get_cb_interest(cbi);
			old = vnode->cb_interest;
			vnode->cb_interest = cbi;
			write_sequnlock(&vnode->cb_lock);
			afs_put_cb_interest(afs_v2net(vnode), cbi);
			afs_put_cb_interest(afs_v2net(vnode), old);
			return 0;
		}

		/* Re-use the one attached to the vnode. */
		if (!cbi && vcbi->server == server) {
			afs_get_cb_interest(vcbi);
			x = cmpxchg(&entry->cb_interest, cbi, vcbi);
			if (x != cbi) {
				cbi = x;
				afs_put_cb_interest(afs_v2net(vnode), vcbi);
			write_lock(&slist->lock);
			if (entry->cb_interest) {
				write_unlock(&slist->lock);
				afs_put_cb_interest(afs_v2net(vnode), cbi);
				goto again;
			}

			entry->cb_interest = cbi;
			write_unlock(&slist->lock);
			return 0;
		}
	}
@@ -72,13 +91,16 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
		list_add_tail(&new->cb_link, &server->cb_interests);
		write_unlock(&server->cb_break_lock);

		x = cmpxchg(&entry->cb_interest, cbi, new);
		if (x == cbi) {
		write_lock(&slist->lock);
		if (!entry->cb_interest) {
			entry->cb_interest = afs_get_cb_interest(new);
			cbi = new;
			new = NULL;
		} else {
			cbi = x;
			afs_put_cb_interest(afs_v2net(vnode), new);
			cbi = afs_get_cb_interest(entry->cb_interest);
		}
		write_unlock(&slist->lock);
		afs_put_cb_interest(afs_v2net(vnode), new);
	}

	ASSERT(cbi);
@@ -88,11 +110,14 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
	 */
	write_seqlock(&vnode->cb_lock);

	vnode->cb_interest = afs_get_cb_interest(cbi);
	old = vnode->cb_interest;
	vnode->cb_interest = cbi;
	vnode->cb_s_break = cbi->server->cb_s_break;
	vnode->cb_v_break = vnode->volume->cb_v_break;
	clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);

	write_sequnlock(&vnode->cb_lock);
	afs_put_cb_interest(afs_v2net(vnode), old);
	return 0;
}

@@ -171,15 +196,26 @@ static void afs_break_one_callback(struct afs_server *server,
		if (cbi->vid != fid->vid)
			continue;

		if (fid->vnode == 0 && fid->unique == 0) {
			/* The callback break applies to an entire volume. */
			struct afs_super_info *as = AFS_FS_S(cbi->sb);
			struct afs_volume *volume = as->volume;

			write_lock(&volume->cb_break_lock);
			volume->cb_v_break++;
			write_unlock(&volume->cb_break_lock);
		} else {
			data.volume = NULL;
			data.fid = *fid;
		inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data);
			inode = ilookup5_nowait(cbi->sb, fid->vnode,
						afs_iget5_test, &data);
			if (inode) {
				vnode = AFS_FS_I(inode);
				afs_break_callback(vnode);
				iput(inode);
			}
		}
	}

	read_unlock(&server->cb_break_lock);
}
@@ -195,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
	ASSERT(server != NULL);
	ASSERTCMP(count, <=, AFSCBMAX);

	/* TODO: Sort the callback break list by volume ID */

	for (; count > 0; callbacks++, count--) {
		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
		       callbacks->fid.vid,
+21 −46
Original line number Diff line number Diff line
@@ -133,21 +133,10 @@ bool afs_cm_incoming_call(struct afs_call *call)
}

/*
 * clean up a cache manager call
 * Clean up a cache manager call.
 */
static void afs_cm_destructor(struct afs_call *call)
{
	_enter("");

	/* Break the callbacks here so that we do it after the final ACK is
	 * received.  The step number here must match the final number in
	 * afs_deliver_cb_callback().
	 */
	if (call->unmarshall == 5) {
		ASSERT(call->cm_server && call->count && call->request);
		afs_break_callbacks(call->cm_server, call->count, call->request);
	}

	kfree(call->buffer);
	call->buffer = NULL;
}
@@ -161,14 +150,14 @@ static void SRXAFSCB_CallBack(struct work_struct *work)

	_enter("");

	/* be sure to send the reply *before* attempting to spam the AFS server
	 * with FSFetchStatus requests on the vnodes with broken callbacks lest
	 * the AFS server get into a vicious cycle of trying to break further
	 * callbacks because it hadn't received completion of the CBCallBack op
	 * yet */
	afs_send_empty_reply(call);

	/* We need to break the callbacks before sending the reply as the
	 * server holds up change visibility till it receives our reply so as
	 * to maintain cache coherency.
	 */
	if (call->cm_server)
		afs_break_callbacks(call->cm_server, call->count, call->request);

	afs_send_empty_reply(call);
	afs_put_call(call);
	_leave("");
}
@@ -180,7 +169,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
{
	struct afs_callback_break *cb;
	struct sockaddr_rxrpc srx;
	struct afs_server *server;
	__be32 *bp;
	int ret, loop;

@@ -267,15 +255,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)

		call->offset = 0;
		call->unmarshall++;

		/* Record that the message was unmarshalled successfully so
		 * that the call destructor can know do the callback breaking
		 * work, even if the final ACK isn't received.
		 *
		 * If the step number changes, then afs_cm_destructor() must be
		 * updated also.
		 */
		call->unmarshall++;
	case 5:
		break;
	}
@@ -286,10 +265,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->cm_server = server;
	call->cm_server = afs_find_server(call->net, &srx);
	if (!call->cm_server)
		trace_afs_cm_no_server(call, &srx);

	return afs_queue_call_work(call);
}
@@ -303,6 +281,7 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)

	_enter("{%p}", call->cm_server);

	if (call->cm_server)
		afs_init_callback_state(call->cm_server);
	afs_send_empty_reply(call);
	afs_put_call(call);
@@ -315,7 +294,6 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
{
	struct sockaddr_rxrpc srx;
	struct afs_server *server;
	int ret;

	_enter("");
@@ -328,10 +306,9 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)

	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->cm_server = server;
	call->cm_server = afs_find_server(call->net, &srx);
	if (!call->cm_server)
		trace_afs_cm_no_server(call, &srx);

	return afs_queue_call_work(call);
}
@@ -341,8 +318,6 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
 */
static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
{
	struct sockaddr_rxrpc srx;
	struct afs_server *server;
	struct afs_uuid *r;
	unsigned loop;
	__be32 *b;
@@ -398,11 +373,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)

	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->cm_server = server;
	rcu_read_lock();
	call->cm_server = afs_find_server_by_uuid(call->net, call->request);
	rcu_read_unlock();
	if (!call->cm_server)
		trace_afs_cm_no_server_u(call, call->request);

	return afs_queue_call_work(call);
}
+26 −28
Original line number Diff line number Diff line
@@ -180,6 +180,7 @@ static int afs_dir_open(struct inode *inode, struct file *file)
 * get reclaimed during the iteration.
 */
static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
	__acquires(&dvnode->validate_lock)
{
	struct afs_read *req;
	loff_t i_size;
@@ -261,18 +262,21 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
	/* If we're going to reload, we need to lock all the pages to prevent
	 * races.
	 */
	if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
	ret = -ERESTARTSYS;
		for (i = 0; i < req->nr_pages; i++)
			if (lock_page_killable(req->pages[i]) < 0)
				goto error_unlock;
	if (down_read_killable(&dvnode->validate_lock) < 0)
		goto error;

	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
		goto success;

	up_read(&dvnode->validate_lock);
	if (down_write_killable(&dvnode->validate_lock) < 0)
		goto error;

	if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
		ret = afs_fetch_data(dvnode, key, req);
		if (ret < 0)
			goto error_unlock_all;
			goto error_unlock;

		task_io_account_read(PAGE_SIZE * req->nr_pages);

@@ -284,33 +288,26 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
		for (i = 0; i < req->nr_pages; i++)
			if (!afs_dir_check_page(dvnode, req->pages[i],
						req->actual_len))
				goto error_unlock_all;
				goto error_unlock;

		// TODO: Trim excess pages

		set_bit(AFS_VNODE_DIR_VALID, &dvnode->flags);
	}

	downgrade_write(&dvnode->validate_lock);
success:
	i = req->nr_pages;
	while (i > 0)
		unlock_page(req->pages[--i]);
	return req;

error_unlock_all:
	i = req->nr_pages;
error_unlock:
	while (i > 0)
		unlock_page(req->pages[--i]);
	up_write(&dvnode->validate_lock);
error:
	afs_put_read(req);
	_leave(" = %d", ret);
	return ERR_PTR(ret);

content_has_grown:
	i = req->nr_pages;
	while (i > 0)
		unlock_page(req->pages[--i]);
	up_write(&dvnode->validate_lock);
	afs_put_read(req);
	goto retry;
}
@@ -473,6 +470,7 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
	}

out:
	up_read(&dvnode->validate_lock);
	afs_put_read(req);
	_leave(" = %d", ret);
	return ret;
@@ -1143,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, dvnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
				      &newfid, &newstatus, &newcb);
		}
@@ -1213,7 +1211,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, dvnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			afs_fs_remove(&fc, dentry->d_name.name, true,
				      data_version);
		}
@@ -1316,7 +1314,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, dvnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			afs_fs_remove(&fc, dentry->d_name.name, false,
				      data_version);
		}
@@ -1373,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, dvnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
				      &newfid, &newstatus, &newcb);
		}
@@ -1443,8 +1441,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
		}

		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
			afs_fs_link(&fc, vnode, dentry->d_name.name, data_version);
		}

@@ -1512,7 +1510,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, dvnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
			afs_fs_symlink(&fc, dentry->d_name.name,
				       content, data_version,
				       &newfid, &newstatus);
@@ -1588,8 +1586,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
			}
		}
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
			fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
			fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
			afs_fs_rename(&fc, old_dentry->d_name.name,
				      new_dvnode, new_dentry->d_name.name,
				      orig_data_version, new_data_version);
+1 −1
Original line number Diff line number Diff line
@@ -238,7 +238,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
	ret = -ERESTARTSYS;
	if (afs_begin_vnode_operation(&fc, vnode, key)) {
		while (afs_select_fileserver(&fc)) {
			fc.cb_break = vnode->cb_break + vnode->cb_s_break;
			fc.cb_break = afs_calc_vnode_cb_break(vnode);
			afs_fs_fetch_data(&fc, desc);
		}

Loading