diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2018-06-02 18:08:11 -0400 | 
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-06-02 18:09:27 -0400 | 
| commit | de52cf922a4a17d0a4cd34d697db62a01c1bd092 (patch) | |
| tree | 1c12675d523eaa34e01499ae2a70dc6983a1918b /fs/afs | |
| parent | 5b86d4ff5dce3271dff54119e06174dc22422903 (diff) | |
| parent | 4776cab43fd3111618112737a257dc3ef368eddd (diff) | |
| download | linux-de52cf922a4a17d0a4cd34d697db62a01c1bd092.tar.bz2 | |
Merge tag 'afs-fixes-20180514' into afs-proc
backmerge AFS fixes that went into mainline and deal with
the conflict in fs/afs/fsclient.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/afs')
| -rw-r--r-- | fs/afs/addr_list.c | 25 | ||||
| -rw-r--r-- | fs/afs/callback.c | 84 | ||||
| -rw-r--r-- | fs/afs/cmservice.c | 67 | ||||
| -rw-r--r-- | fs/afs/dir.c | 54 | ||||
| -rw-r--r-- | fs/afs/file.c | 2 | ||||
| -rw-r--r-- | fs/afs/flock.c | 6 | ||||
| -rw-r--r-- | fs/afs/fsclient.c | 28 | ||||
| -rw-r--r-- | fs/afs/inode.c | 19 | ||||
| -rw-r--r-- | fs/afs/internal.h | 25 | ||||
| -rw-r--r-- | fs/afs/rotate.c | 20 | ||||
| -rw-r--r-- | fs/afs/rxrpc.c | 18 | ||||
| -rw-r--r-- | fs/afs/security.c | 7 | ||||
| -rw-r--r-- | fs/afs/server.c | 21 | ||||
| -rw-r--r-- | fs/afs/server_list.c | 7 | ||||
| -rw-r--r-- | fs/afs/super.c | 4 | ||||
| -rw-r--r-- | fs/afs/write.c | 2 | 
16 files changed, 222 insertions, 167 deletions
| diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index 3bedfed608a2..7587fb665ff1 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -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 != ']') -				goto bad_address; +		if (stop != q) +			goto bad_address; + +		p = q; +		if (q < end && *q == ']')  			p++; -		}  		if (p < end) {  			if (*p == '+') { diff --git a/fs/afs/callback.c b/fs/afs/callback.c index abd9a84f4e88..571437dcb252 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -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 @@ again:  		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 @@ again:  	 */  	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,13 +196,24 @@ static void afs_break_one_callback(struct afs_server *server,  		if (cbi->vid != fid->vid)  			continue; -		data.volume = NULL; -		data.fid = *fid; -		inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data); -		if (inode) { -			vnode = AFS_FS_I(inode); -			afs_break_callback(vnode); -			iput(inode); +		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); +			if (inode) { +				vnode = AFS_FS_I(inode); +				afs_break_callback(vnode); +				iput(inode); +			}  		}  	} @@ -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, diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 4c89b1f4c02b..5894101cdd9a 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -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_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,7 +281,8 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)  	_enter("{%p}", call->cm_server); -	afs_init_callback_state(call->cm_server); +	if (call->cm_server) +		afs_init_callback_state(call->cm_server);  	afs_send_empty_reply(call);  	afs_put_call(call);  	_leave(""); @@ -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);  } diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 5889f70d4d27..7d623008157f 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -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 @@ retry:  	/* 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; +	ret = -ERESTARTSYS; +	if (down_read_killable(&dvnode->validate_lock) < 0) +		goto error; -		if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) -			goto success; +	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 @@ retry:  		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); diff --git a/fs/afs/file.c b/fs/afs/file.c index c24c08016dd9..7d4f26198573 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -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);  		} diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 7a0e017070ec..dc62d15a964b 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -86,7 +86,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,  	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_set_lock(&fc, type);  		} @@ -117,7 +117,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)  	ret = -ERESTARTSYS;  	if (afs_begin_vnode_operation(&fc, vnode, key)) {  		while (afs_select_current_fileserver(&fc)) { -			fc.cb_break = vnode->cb_break + vnode->cb_s_break; +			fc.cb_break = afs_calc_vnode_cb_break(vnode);  			afs_fs_extend_lock(&fc);  		} @@ -148,7 +148,7 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)  	ret = -ERESTARTSYS;  	if (afs_begin_vnode_operation(&fc, vnode, key)) {  		while (afs_select_current_fileserver(&fc)) { -			fc.cb_break = vnode->cb_break + vnode->cb_s_break; +			fc.cb_break = afs_calc_vnode_cb_break(vnode);  			afs_fs_release_lock(&fc);  		} diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b695d9e16c65..2a80bbcc31f9 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -134,17 +134,35 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,  				     struct afs_read *read_req)  {  	const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp; +	bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus);  	u64 data_version, size;  	u32 type, abort_code;  	u8 flags = 0; +	abort_code = ntohl(xdr->abort_code); +  	if (xdr->if_version != htonl(AFS_FSTATUS_VERSION)) { +		if (xdr->if_version == htonl(0) && +		    abort_code != 0 && +		    inline_error) { +			/* The OpenAFS fileserver has a bug in FS.InlineBulkStatus +			 * whereby it doesn't set the interface version in the error +			 * case. +			 */ +			status->abort_code = abort_code; +			return 0; +		} +  		pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));  		goto bad;  	} +	if (abort_code != 0 && inline_error) { +		status->abort_code = abort_code; +		return 0; +	} +  	type = ntohl(xdr->type); -	abort_code = ntohl(xdr->abort_code);  	switch (type) {  	case AFS_FTYPE_FILE:  	case AFS_FTYPE_DIR: @@ -161,12 +179,6 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,  		}  		status->type = type;  		break; -	case AFS_FTYPE_INVALID: -		if (abort_code != 0) { -			status->abort_code = abort_code; -			return 0; -		} -		/* Fall through */  	default:  		goto bad;  	} @@ -261,7 +273,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,  	write_seqlock(&vnode->cb_lock); -	if (call->cb_break == (vnode->cb_break + cbi->server->cb_s_break)) { +	if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {  		vnode->cb_version	= ntohl(*bp++);  		cb_expiry		= ntohl(*bp++);  		vnode->cb_type		= ntohl(*bp++); diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 06194cfe9724..479b7fdda124 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -108,7 +108,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)  	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_file_status(&fc, NULL, new_inode);  		} @@ -393,15 +393,18 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)  	read_seqlock_excl(&vnode->cb_lock);  	if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { -		if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { +		if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break || +		    vnode->cb_v_break != vnode->volume->cb_v_break) {  			vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; +			vnode->cb_v_break = vnode->volume->cb_v_break; +			valid = false;  		} else if (vnode->status.type == AFS_FTYPE_DIR &&  			   test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) &&  			   vnode->cb_expires_at - 10 > now) { -				valid = true; +			valid = true;  		} else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&  			   vnode->cb_expires_at - 10 > now) { -				valid = true; +			valid = true;  		}  	} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {  		valid = true; @@ -415,7 +418,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)  	if (valid)  		goto valid; -	mutex_lock(&vnode->validate_lock); +	down_write(&vnode->validate_lock);  	/* if the promise has expired, we need to check the server again to get  	 * a new promise - note that if the (parent) directory's metadata was @@ -444,13 +447,13 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)  	 * different */  	if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))  		afs_zap_data(vnode); -	mutex_unlock(&vnode->validate_lock); +	up_write(&vnode->validate_lock);  valid:  	_leave(" = 0");  	return 0;  error_unlock: -	mutex_unlock(&vnode->validate_lock); +	up_write(&vnode->validate_lock);  	_leave(" = %d", ret);  	return ret;  } @@ -574,7 +577,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)  	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_setattr(&fc, attr);  		} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index e6cef5702ae2..f0cd7ed42c1e 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -398,6 +398,7 @@ struct afs_server {  #define AFS_SERVER_FL_PROBED	5		/* The fileserver has been probed */  #define AFS_SERVER_FL_PROBING	6		/* Fileserver is being probed */  #define AFS_SERVER_FL_NO_IBULK	7		/* Fileserver doesn't support FS.InlineBulkStatus */ +#define AFS_SERVER_FL_MAY_HAVE_CB 8		/* May have callbacks on this fileserver */  	atomic_t		usage;  	u32			addr_version;	/* Address list version */ @@ -435,6 +436,7 @@ struct afs_server_list {  	unsigned short		index;		/* Server currently in use */  	unsigned short		vnovol_mask;	/* Servers to be skipped due to VNOVOL */  	unsigned int		seq;		/* Set to ->servers_seq when installed */ +	rwlock_t		lock;  	struct afs_server_entry	servers[];  }; @@ -461,6 +463,9 @@ struct afs_volume {  	rwlock_t		servers_lock;	/* Lock for ->servers */  	unsigned int		servers_seq;	/* Incremented each time ->servers changes */ +	unsigned		cb_v_break;	/* Break-everything counter. */ +	rwlock_t		cb_break_lock; +  	afs_voltype_t		type;		/* type of volume */  	short			error;  	char			type_force;	/* force volume type (suppress R/O -> R/W) */ @@ -496,7 +501,7 @@ struct afs_vnode {  #endif  	struct afs_permits __rcu *permit_cache;	/* cache of permits so far obtained */  	struct mutex		io_lock;	/* Lock for serialising I/O on this mutex */ -	struct mutex		validate_lock;	/* lock for validating this vnode */ +	struct rw_semaphore	validate_lock;	/* lock for validating this vnode */  	spinlock_t		wb_lock;	/* lock for wb_keys */  	spinlock_t		lock;		/* waitqueue/flags lock */  	unsigned long		flags; @@ -521,6 +526,7 @@ struct afs_vnode {  	/* outstanding callback notification on this file */  	struct afs_cb_interest	*cb_interest;	/* Server on which this resides */  	unsigned int		cb_s_break;	/* Mass break counter on ->server */ +	unsigned int		cb_v_break;	/* Mass break counter on ->volume */  	unsigned int		cb_break;	/* Break counter on vnode */  	seqlock_t		cb_lock;	/* Lock for ->cb_interest, ->status, ->cb_*break */ @@ -650,16 +656,29 @@ extern void afs_init_callback_state(struct afs_server *);  extern void afs_break_callback(struct afs_vnode *);  extern void afs_break_callbacks(struct afs_server *, size_t, struct afs_callback_break*); -extern int afs_register_server_cb_interest(struct afs_vnode *, struct afs_server_entry *); +extern int afs_register_server_cb_interest(struct afs_vnode *, +					   struct afs_server_list *, unsigned int);  extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *);  extern void afs_clear_callback_interests(struct afs_net *, struct afs_server_list *);  static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest *cbi)  { -	refcount_inc(&cbi->usage); +	if (cbi) +		refcount_inc(&cbi->usage);  	return cbi;  } +static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode) +{ +	return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break; +} + +static inline unsigned int afs_cb_break_sum(struct afs_vnode *vnode, +					    struct afs_cb_interest *cbi) +{ +	return vnode->cb_break + cbi->server->cb_s_break + vnode->volume->cb_v_break; +} +  /*   * cell.c   */ diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index ac0feac9d746..e065bc0768e6 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -179,7 +179,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)  			 */  			if (fc->flags & AFS_FS_CURSOR_VNOVOL) {  				fc->ac.error = -EREMOTEIO; -				goto failed; +				goto next_server;  			}  			write_lock(&vnode->volume->servers_lock); @@ -201,7 +201,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)  			 */  			if (vnode->volume->servers == fc->server_list) {  				fc->ac.error = -EREMOTEIO; -				goto failed; +				goto next_server;  			}  			/* Try again */ @@ -350,8 +350,8 @@ use_server:  	 * break request before we've finished decoding the reply and  	 * installing the vnode.  	 */ -	fc->ac.error = afs_register_server_cb_interest( -		vnode, &fc->server_list->servers[fc->index]); +	fc->ac.error = afs_register_server_cb_interest(vnode, fc->server_list, +						       fc->index);  	if (fc->ac.error < 0)  		goto failed; @@ -369,8 +369,16 @@ use_server:  	if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) {  		fc->ac.alist = afs_get_addrlist(alist); -		if (!afs_probe_fileserver(fc)) -			goto failed; +		if (!afs_probe_fileserver(fc)) { +			switch (fc->ac.error) { +			case -ENOMEM: +			case -ERESTARTSYS: +			case -EINTR: +				goto failed; +			default: +				goto next_server; +			} +		}  	}  	if (!fc->ac.alist) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index e58fa0e15798..a1b18082991b 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -41,6 +41,7 @@ int afs_open_socket(struct afs_net *net)  {  	struct sockaddr_rxrpc srx;  	struct socket *socket; +	unsigned int min_level;  	int ret;  	_enter(""); @@ -60,6 +61,12 @@ int afs_open_socket(struct afs_net *net)  	srx.transport.sin6.sin6_family	= AF_INET6;  	srx.transport.sin6.sin6_port	= htons(AFS_CM_PORT); +	min_level = RXRPC_SECURITY_ENCRYPT; +	ret = kernel_setsockopt(socket, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL, +				(void *)&min_level, sizeof(min_level)); +	if (ret < 0) +		goto error_2; +  	ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));  	if (ret == -EADDRINUSE) {  		srx.transport.sin6.sin6_port = 0; @@ -482,8 +489,12 @@ static void afs_deliver_to_call(struct afs_call *call)  		state = READ_ONCE(call->state);  		switch (ret) {  		case 0: -			if (state == AFS_CALL_CL_PROC_REPLY) +			if (state == AFS_CALL_CL_PROC_REPLY) { +				if (call->cbi) +					set_bit(AFS_SERVER_FL_MAY_HAVE_CB, +						&call->cbi->server->flags);  				goto call_complete; +			}  			ASSERTCMP(state, >, AFS_CALL_CL_PROC_REPLY);  			goto done;  		case -EINPROGRESS: @@ -493,11 +504,6 @@ static void afs_deliver_to_call(struct afs_call *call)  		case -ECONNABORTED:  			ASSERTCMP(state, ==, AFS_CALL_COMPLETE);  			goto done; -		case -ENOTCONN: -			abort_code = RX_CALL_DEAD; -			rxrpc_kernel_abort_call(call->net->socket, call->rxcall, -						abort_code, ret, "KNC"); -			goto local_abort;  		case -ENOTSUPP:  			abort_code = RXGEN_OPCODE;  			rxrpc_kernel_abort_call(call->net->socket, call->rxcall, diff --git a/fs/afs/security.c b/fs/afs/security.c index cea2fff313dc..1992b0ffa543 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -147,8 +147,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,  					break;  				} -				if (cb_break != (vnode->cb_break + -						 vnode->cb_interest->server->cb_s_break)) { +				if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) {  					changed = true;  					break;  				} @@ -178,7 +177,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,  		}  	} -	if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break)) +	if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest))  		goto someone_else_changed_it;  	/* We need a ref on any permits list we want to copy as we'll have to @@ -257,7 +256,7 @@ found:  	spin_lock(&vnode->lock);  	zap = rcu_access_pointer(vnode->permit_cache); -	if (cb_break == (vnode->cb_break + vnode->cb_interest->server->cb_s_break) && +	if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) &&  	    zap == permits)  		rcu_assign_pointer(vnode->permit_cache, replacement);  	else diff --git a/fs/afs/server.c b/fs/afs/server.c index 629c74986cff..3af4625e2f8c 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -67,12 +67,6 @@ struct afs_server *afs_find_server(struct afs_net *net,  							      sizeof(struct in6_addr));  					if (diff == 0)  						goto found; -					if (diff < 0) { -						// TODO: Sort the list -						//if (i == alist->nr_ipv4) -						//	goto not_found; -						break; -					}  				}  			}  		} else { @@ -87,17 +81,10 @@ struct afs_server *afs_find_server(struct afs_net *net,  							(u32 __force)b->sin6_addr.s6_addr32[3]);  					if (diff == 0)  						goto found; -					if (diff < 0) { -						// TODO: Sort the list -						//if (i == 0) -						//	goto not_found; -						break; -					}  				}  			}  		} -	//not_found:  		server = NULL;  	found:  		if (server && !atomic_inc_not_zero(&server->usage)) @@ -395,14 +382,16 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server)  	struct afs_addr_list *alist = rcu_access_pointer(server->addresses);  	struct afs_addr_cursor ac = {  		.alist	= alist, -		.addr	= &alist->addrs[0],  		.start	= alist->index, -		.index	= alist->index, +		.index	= 0, +		.addr	= &alist->addrs[alist->index],  		.error	= 0,  	};  	_enter("%p", server); -	afs_fs_give_up_all_callbacks(net, server, &ac, NULL); +	if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags)) +		afs_fs_give_up_all_callbacks(net, server, &ac, NULL); +  	call_rcu(&server->rcu, afs_server_rcu);  	afs_dec_servers_outstanding(net);  } diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index 0f8dc4c8f07c..8a5760aa5832 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -49,6 +49,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,  		goto error;  	refcount_set(&slist->usage, 1); +	rwlock_init(&slist->lock);  	/* Make sure a records exists for each server in the list. */  	for (i = 0; i < vldb->nr_servers; i++) { @@ -64,9 +65,11 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,  			goto error_2;  		} -		/* Insertion-sort by server pointer */ +		/* Insertion-sort by UUID */  		for (j = 0; j < slist->nr_servers; j++) -			if (slist->servers[j].server >= server) +			if (memcmp(&slist->servers[j].server->uuid, +				   &server->uuid, +				   sizeof(server->uuid)) >= 0)  				break;  		if (j < slist->nr_servers) {  			if (slist->servers[j].server == server) { diff --git a/fs/afs/super.c b/fs/afs/super.c index 593820372848..8707d867334e 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -594,7 +594,7 @@ static void afs_i_init_once(void *_vnode)  	memset(vnode, 0, sizeof(*vnode));  	inode_init_once(&vnode->vfs_inode);  	mutex_init(&vnode->io_lock); -	mutex_init(&vnode->validate_lock); +	init_rwsem(&vnode->validate_lock);  	spin_lock_init(&vnode->wb_lock);  	spin_lock_init(&vnode->lock);  	INIT_LIST_HEAD(&vnode->wb_keys); @@ -692,7 +692,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)  	if (afs_begin_vnode_operation(&fc, vnode, key)) {  		fc.flags |= AFS_FS_CURSOR_NO_VSLEEP;  		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_get_volume_status(&fc, &vs);  		} diff --git a/fs/afs/write.c b/fs/afs/write.c index c164698dc304..8b39e6ebb40b 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -351,7 +351,7 @@ found_key:  	ret = -ERESTARTSYS;  	if (afs_begin_vnode_operation(&fc, vnode, wbk->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_store_data(&fc, mapping, first, last, offset, to);  		} |