mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	afs: Implement VL server rotation
Track VL servers as independent entities rather than lumping all their
addresses together into one set and implement server-level rotation by:
 (1) Add the concept of a VL server list, where each server has its own
     separate address list.  This code is similar to the FS server list.
 (2) Use the DNS resolver to retrieve a set of servers and their associated
     addresses, ports, preference and weight ratings.
 (3) In the case of a legacy DNS resolver or an address list given directly
     through /proc/net/afs/cells, create a list containing just a dummy
     server record and attach all the addresses to that.
 (4) Implement a simple rotation policy, for the moment ignoring the
     priorities and weights assigned to the servers.
 (5) Show the address list through /proc/net/afs/<cell>/vlservers.  This
     also displays the source and status of the data as indicated by the
     upcall.
Signed-off-by: David Howells <dhowells@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									e7f680f45b
								
							
						
					
					
						commit
						0a5143f2f8
					
				
					 11 changed files with 905 additions and 218 deletions
				
			
		|  | @ -29,6 +29,8 @@ kafs-y := \ | |||
| 	super.o \
 | ||||
| 	netdevices.o \
 | ||||
| 	vlclient.o \
 | ||||
| 	vl_rotate.o \
 | ||||
| 	vl_list.o \
 | ||||
| 	volume.o \
 | ||||
| 	write.o \
 | ||||
| 	xattr.o | ||||
|  |  | |||
|  | @ -64,19 +64,25 @@ struct afs_addr_list *afs_alloc_addrlist(unsigned int nr, | |||
| /*
 | ||||
|  * Parse a text string consisting of delimited addresses. | ||||
|  */ | ||||
| struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | ||||
| 					   char delim, | ||||
| 					   unsigned short service, | ||||
| 					   unsigned short port) | ||||
| struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net, | ||||
| 					       const char *text, size_t len, | ||||
| 					       char delim, | ||||
| 					       unsigned short service, | ||||
| 					       unsigned short port) | ||||
| { | ||||
| 	struct afs_vlserver_list *vllist; | ||||
| 	struct afs_addr_list *alist; | ||||
| 	const char *p, *end = text + len; | ||||
| 	const char *problem; | ||||
| 	unsigned int nr = 0; | ||||
| 	int ret = -ENOMEM; | ||||
| 
 | ||||
| 	_enter("%*.*s,%c", (int)len, (int)len, text, delim); | ||||
| 
 | ||||
| 	if (!len) | ||||
| 	if (!len) { | ||||
| 		_leave(" = -EDESTADDRREQ [empty]"); | ||||
| 		return ERR_PTR(-EDESTADDRREQ); | ||||
| 	} | ||||
| 
 | ||||
| 	if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len))) | ||||
| 		delim = ','; | ||||
|  | @ -84,18 +90,24 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | |||
| 	/* Count the addresses */ | ||||
| 	p = text; | ||||
| 	do { | ||||
| 		if (!*p) | ||||
| 			return ERR_PTR(-EINVAL); | ||||
| 		if (!*p) { | ||||
| 			problem = "nul"; | ||||
| 			goto inval; | ||||
| 		} | ||||
| 		if (*p == delim) | ||||
| 			continue; | ||||
| 		nr++; | ||||
| 		if (*p == '[') { | ||||
| 			p++; | ||||
| 			if (p == end) | ||||
| 				return ERR_PTR(-EINVAL); | ||||
| 			if (p == end) { | ||||
| 				problem = "brace1"; | ||||
| 				goto inval; | ||||
| 			} | ||||
| 			p = memchr(p, ']', end - p); | ||||
| 			if (!p) | ||||
| 				return ERR_PTR(-EINVAL); | ||||
| 			if (!p) { | ||||
| 				problem = "brace2"; | ||||
| 				goto inval; | ||||
| 			} | ||||
| 			p++; | ||||
| 			if (p >= end) | ||||
| 				break; | ||||
|  | @ -109,10 +121,19 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | |||
| 
 | ||||
| 	_debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES); | ||||
| 
 | ||||
| 	alist = afs_alloc_addrlist(nr, service, port); | ||||
| 	if (!alist) | ||||
| 	vllist = afs_alloc_vlserver_list(1); | ||||
| 	if (!vllist) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	vllist->nr_servers = 1; | ||||
| 	vllist->servers[0].server = afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT); | ||||
| 	if (!vllist->servers[0].server) | ||||
| 		goto error_vl; | ||||
| 
 | ||||
| 	alist = afs_alloc_addrlist(nr, service, AFS_VL_PORT); | ||||
| 	if (!alist) | ||||
| 		goto error; | ||||
| 
 | ||||
| 	/* Extract the addresses */ | ||||
| 	p = text; | ||||
| 	do { | ||||
|  | @ -135,17 +156,21 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | |||
| 					break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) | ||||
| 		if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) { | ||||
| 			family = AF_INET; | ||||
| 		else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) | ||||
| 		} else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) { | ||||
| 			family = AF_INET6; | ||||
| 		else | ||||
| 			goto bad_address; | ||||
| 
 | ||||
| 		if (stop != q) | ||||
| 		} else { | ||||
| 			problem = "family"; | ||||
| 			goto bad_address; | ||||
| 		} | ||||
| 
 | ||||
| 		p = q; | ||||
| 		if (stop != p) { | ||||
| 			problem = "nostop"; | ||||
| 			goto bad_address; | ||||
| 		} | ||||
| 
 | ||||
| 		if (q < end && *q == ']') | ||||
| 			p++; | ||||
| 
 | ||||
|  | @ -154,18 +179,23 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | |||
| 				/* Port number specification "+1234" */ | ||||
| 				xport = 0; | ||||
| 				p++; | ||||
| 				if (p >= end || !isdigit(*p)) | ||||
| 				if (p >= end || !isdigit(*p)) { | ||||
| 					problem = "port"; | ||||
| 					goto bad_address; | ||||
| 				} | ||||
| 				do { | ||||
| 					xport *= 10; | ||||
| 					xport += *p - '0'; | ||||
| 					if (xport > 65535) | ||||
| 					if (xport > 65535) { | ||||
| 						problem = "pval"; | ||||
| 						goto bad_address; | ||||
| 					} | ||||
| 					p++; | ||||
| 				} while (p < end && isdigit(*p)); | ||||
| 			} else if (*p == delim) { | ||||
| 				p++; | ||||
| 			} else { | ||||
| 				problem = "weird"; | ||||
| 				goto bad_address; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -177,12 +207,23 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, | |||
| 
 | ||||
| 	} while (p < end); | ||||
| 
 | ||||
| 	rcu_assign_pointer(vllist->servers[0].server->addresses, alist); | ||||
| 	_leave(" = [nr %u]", alist->nr_addrs); | ||||
| 	return alist; | ||||
| 	return vllist; | ||||
| 
 | ||||
| bad_address: | ||||
| 	kfree(alist); | ||||
| inval: | ||||
| 	_leave(" = -EINVAL [%s %zu %*.*s]", | ||||
| 	       problem, p - text, (int)len, (int)len, text); | ||||
| 	return ERR_PTR(-EINVAL); | ||||
| bad_address: | ||||
| 	_leave(" = -EINVAL [%s %zu %*.*s]", | ||||
| 	       problem, p - text, (int)len, (int)len, text); | ||||
| 	ret = -EINVAL; | ||||
| error: | ||||
| 	afs_put_addrlist(alist); | ||||
| error_vl: | ||||
| 	afs_put_vlserverlist(net, vllist); | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -201,30 +242,34 @@ static int afs_cmp_addr_list(const struct afs_addr_list *a1, | |||
| /*
 | ||||
|  * Perform a DNS query for VL servers and build a up an address list. | ||||
|  */ | ||||
| struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) | ||||
| struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	char *vllist = NULL; | ||||
| 	struct afs_vlserver_list *vllist; | ||||
| 	char *result = NULL; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	_enter("%s", cell->name); | ||||
| 
 | ||||
| 	ret = dns_query("afsdb", cell->name, cell->name_len, | ||||
| 			"", &vllist, _expiry); | ||||
| 	if (ret < 0) | ||||
| 	ret = dns_query("afsdb", cell->name, cell->name_len, "srv=1", | ||||
| 			&result, _expiry); | ||||
| 	if (ret < 0) { | ||||
| 		_leave(" = %d [dns]", ret); | ||||
| 		return ERR_PTR(ret); | ||||
| 
 | ||||
| 	alist = afs_parse_text_addrs(vllist, strlen(vllist), ',', | ||||
| 				     VL_SERVICE, AFS_VL_PORT); | ||||
| 	if (IS_ERR(alist)) { | ||||
| 		kfree(vllist); | ||||
| 		if (alist != ERR_PTR(-ENOMEM)) | ||||
| 			pr_err("Failed to parse DNS data\n"); | ||||
| 		return alist; | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(vllist); | ||||
| 	return alist; | ||||
| 	if (*_expiry == 0) | ||||
| 		*_expiry = ktime_get_real_seconds() + 60; | ||||
| 
 | ||||
| 	if (ret > 1 && result[0] == 0) | ||||
| 		vllist = afs_extract_vlserver_list(cell, result, ret); | ||||
| 	else | ||||
| 		vllist = afs_parse_text_addrs(cell->net, result, ret, ',', | ||||
| 					      VL_SERVICE, AFS_VL_PORT); | ||||
| 	kfree(result); | ||||
| 	if (IS_ERR(vllist) && vllist != ERR_PTR(-ENOMEM)) | ||||
| 		pr_err("Failed to parse DNS data %ld\n", PTR_ERR(vllist)); | ||||
| 
 | ||||
| 	return vllist; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -347,43 +392,3 @@ int afs_end_cursor(struct afs_addr_cursor *ac) | |||
| 	ac->begun = false; | ||||
| 	return ac->error; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Set the address cursor for iterating over VL servers. | ||||
|  */ | ||||
| int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!rcu_access_pointer(cell->vl_addrs)) { | ||||
| 		ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET, | ||||
| 				  TASK_INTERRUPTIBLE); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		if (!rcu_access_pointer(cell->vl_addrs) && | ||||
| 		    ktime_get_real_seconds() < cell->dns_expiry) | ||||
| 			return cell->error; | ||||
| 	} | ||||
| 
 | ||||
| 	read_lock(&cell->vl_addrs_lock); | ||||
| 	alist = rcu_dereference_protected(cell->vl_addrs, | ||||
| 					  lockdep_is_held(&cell->vl_addrs_lock)); | ||||
| 	if (alist->nr_addrs > 0) | ||||
| 		afs_get_addrlist(alist); | ||||
| 	else | ||||
| 		alist = NULL; | ||||
| 	read_unlock(&cell->vl_addrs_lock); | ||||
| 
 | ||||
| 	if (!alist) | ||||
| 		return -EDESTADDRREQ; | ||||
| 
 | ||||
| 	ac->alist = alist; | ||||
| 	ac->addr = NULL; | ||||
| 	ac->start = READ_ONCE(alist->index); | ||||
| 	ac->index = ac->start; | ||||
| 	ac->error = 0; | ||||
| 	ac->begun = false; | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -119,7 +119,7 @@ struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net, | |||
|  */ | ||||
| static struct afs_cell *afs_alloc_cell(struct afs_net *net, | ||||
| 				       const char *name, unsigned int namelen, | ||||
| 				       const char *vllist) | ||||
| 				       const char *addresses) | ||||
| { | ||||
| 	struct afs_cell *cell; | ||||
| 	int i, ret; | ||||
|  | @ -134,7 +134,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, | |||
| 	if (namelen == 5 && memcmp(name, "@cell", 5) == 0) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	_enter("%*.*s,%s", namelen, namelen, name, vllist); | ||||
| 	_enter("%*.*s,%s", namelen, namelen, name, addresses); | ||||
| 
 | ||||
| 	cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL); | ||||
| 	if (!cell) { | ||||
|  | @ -153,22 +153,23 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, | |||
| 		       (1 << AFS_CELL_FL_NO_LOOKUP_YET)); | ||||
| 	INIT_LIST_HEAD(&cell->proc_volumes); | ||||
| 	rwlock_init(&cell->proc_lock); | ||||
| 	rwlock_init(&cell->vl_addrs_lock); | ||||
| 	rwlock_init(&cell->vl_servers_lock); | ||||
| 
 | ||||
| 	/* Fill in the VL server list if we were given a list of addresses to
 | ||||
| 	 * use. | ||||
| 	 */ | ||||
| 	if (vllist) { | ||||
| 		struct afs_addr_list *alist; | ||||
| 	if (addresses) { | ||||
| 		struct afs_vlserver_list *vllist; | ||||
| 
 | ||||
| 		alist = afs_parse_text_addrs(vllist, strlen(vllist), ':', | ||||
| 					     VL_SERVICE, AFS_VL_PORT); | ||||
| 		if (IS_ERR(alist)) { | ||||
| 			ret = PTR_ERR(alist); | ||||
| 		vllist = afs_parse_text_addrs(net, | ||||
| 					      addresses, strlen(addresses), ':', | ||||
| 					      VL_SERVICE, AFS_VL_PORT); | ||||
| 		if (IS_ERR(vllist)) { | ||||
| 			ret = PTR_ERR(vllist); | ||||
| 			goto parse_failed; | ||||
| 		} | ||||
| 
 | ||||
| 		rcu_assign_pointer(cell->vl_addrs, alist); | ||||
| 		rcu_assign_pointer(cell->vl_servers, vllist); | ||||
| 		cell->dns_expiry = TIME64_MAX; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -356,14 +357,14 @@ int afs_cell_init(struct afs_net *net, const char *rootcell) | |||
|  */ | ||||
| static void afs_update_cell(struct afs_cell *cell) | ||||
| { | ||||
| 	struct afs_addr_list *alist, *old; | ||||
| 	struct afs_vlserver_list *vllist, *old; | ||||
| 	time64_t now, expiry; | ||||
| 
 | ||||
| 	_enter("%s", cell->name); | ||||
| 
 | ||||
| 	alist = afs_dns_query(cell, &expiry); | ||||
| 	if (IS_ERR(alist)) { | ||||
| 		switch (PTR_ERR(alist)) { | ||||
| 	vllist = afs_dns_query(cell, &expiry); | ||||
| 	if (IS_ERR(vllist)) { | ||||
| 		switch (PTR_ERR(vllist)) { | ||||
| 		case -ENODATA: | ||||
| 			/* The DNS said that the cell does not exist */ | ||||
| 			set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags); | ||||
|  | @ -387,12 +388,12 @@ static void afs_update_cell(struct afs_cell *cell) | |||
| 		/* Exclusion on changing vl_addrs is achieved by a
 | ||||
| 		 * non-reentrant work item. | ||||
| 		 */ | ||||
| 		old = rcu_dereference_protected(cell->vl_addrs, true); | ||||
| 		rcu_assign_pointer(cell->vl_addrs, alist); | ||||
| 		old = rcu_dereference_protected(cell->vl_servers, true); | ||||
| 		rcu_assign_pointer(cell->vl_servers, vllist); | ||||
| 		cell->dns_expiry = expiry; | ||||
| 
 | ||||
| 		if (old) | ||||
| 			afs_put_addrlist(old); | ||||
| 			afs_put_vlserverlist(cell->net, old); | ||||
| 	} | ||||
| 
 | ||||
| 	if (test_and_clear_bit(AFS_CELL_FL_NO_LOOKUP_YET, &cell->flags)) | ||||
|  | @ -414,7 +415,7 @@ static void afs_cell_destroy(struct rcu_head *rcu) | |||
| 
 | ||||
| 	ASSERTCMP(atomic_read(&cell->usage), ==, 0); | ||||
| 
 | ||||
| 	afs_put_addrlist(rcu_access_pointer(cell->vl_addrs)); | ||||
| 	afs_put_vlserverlist(cell->net, rcu_access_pointer(cell->vl_servers)); | ||||
| 	key_put(cell->anonymous_key); | ||||
| 	kfree(cell); | ||||
| 
 | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ static int afs_probe_cell_name(struct dentry *dentry) | |||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = dns_query("afsdb", name, len, "", NULL, NULL); | ||||
| 	ret = dns_query("afsdb", name, len, "srv=1", NULL, NULL); | ||||
| 	if (ret == -ENODATA) | ||||
| 		ret = -EDESTADDRREQ; | ||||
| 	return ret; | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include <linux/backing-dev.h> | ||||
| #include <linux/uuid.h> | ||||
| #include <linux/mm_types.h> | ||||
| #include <linux/dns_resolver.h> | ||||
| #include <net/net_namespace.h> | ||||
| #include <net/netns/generic.h> | ||||
| #include <net/sock.h> | ||||
|  | @ -77,6 +78,8 @@ struct afs_addr_list { | |||
| 	unsigned char		nr_addrs; | ||||
| 	unsigned char		index;		/* Address currently in use */ | ||||
| 	unsigned char		nr_ipv4;	/* Number of IPv4 addresses */ | ||||
| 	enum dns_record_source	source:8; | ||||
| 	enum dns_lookup_status	status:8; | ||||
| 	unsigned long		probed;		/* Mask of servers that have been probed */ | ||||
| 	unsigned long		yfs;		/* Mask of servers that are YFS */ | ||||
| 	struct sockaddr_rxrpc	addrs[]; | ||||
|  | @ -355,12 +358,51 @@ struct afs_cell { | |||
| 	rwlock_t		proc_lock; | ||||
| 
 | ||||
| 	/* VL server list. */ | ||||
| 	rwlock_t		vl_addrs_lock;	/* Lock on vl_addrs */ | ||||
| 	struct afs_addr_list	__rcu *vl_addrs; /* List of VL servers */ | ||||
| 	rwlock_t		vl_servers_lock; /* Lock on vl_servers */ | ||||
| 	struct afs_vlserver_list __rcu *vl_servers; | ||||
| 
 | ||||
| 	u8			name_len;	/* Length of name */ | ||||
| 	char			name[64 + 1];	/* Cell name, case-flattened and NUL-padded */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Volume Location server record. | ||||
|  */ | ||||
| struct afs_vlserver { | ||||
| 	struct rcu_head		rcu; | ||||
| 	struct afs_addr_list	__rcu *addresses; /* List of addresses for this VL server */ | ||||
| 	unsigned long		flags; | ||||
| #define AFS_VLSERVER_FL_PROBED	0		/* The VL server has been probed */ | ||||
| #define AFS_VLSERVER_FL_PROBING	1		/* VL server is being probed */ | ||||
| 	rwlock_t		lock;		/* Lock on addresses */ | ||||
| 	atomic_t		usage; | ||||
| 	u16			name_len;	/* Length of name */ | ||||
| 	u16			port; | ||||
| 	char			name[];		/* Server name, case-flattened */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Weighted list of Volume Location servers. | ||||
|  */ | ||||
| struct afs_vlserver_entry { | ||||
| 	u16			priority;	/* Preference (as SRV) */ | ||||
| 	u16			weight;		/* Weight (as SRV) */ | ||||
| 	enum dns_record_source	source:8; | ||||
| 	enum dns_lookup_status	status:8; | ||||
| 	struct afs_vlserver	*server; | ||||
| }; | ||||
| 
 | ||||
| struct afs_vlserver_list { | ||||
| 	struct rcu_head		rcu; | ||||
| 	atomic_t		usage; | ||||
| 	u8			nr_servers; | ||||
| 	u8			index;		/* Server currently in use */ | ||||
| 	enum dns_record_source	source:8; | ||||
| 	enum dns_lookup_status	status:8; | ||||
| 	rwlock_t		lock; | ||||
| 	struct afs_vlserver_entry servers[]; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cached VLDB entry. | ||||
|  * | ||||
|  | @ -616,6 +658,23 @@ struct afs_addr_cursor { | |||
| 	bool			responded;	/* T if the current address responded */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cursor for iterating over a set of volume location servers. | ||||
|  */ | ||||
| struct afs_vl_cursor { | ||||
| 	struct afs_addr_cursor	ac; | ||||
| 	struct afs_cell		*cell;		/* The cell we're querying */ | ||||
| 	struct afs_vlserver_list *server_list;	/* Current server list (pins ref) */ | ||||
| 	struct key		*key;		/* Key for the server */ | ||||
| 	unsigned char		start;		/* Initial index in server list */ | ||||
| 	unsigned char		index;		/* Number of servers tried beyond start */ | ||||
| 	short			error; | ||||
| 	unsigned short		flags; | ||||
| #define AFS_VL_CURSOR_STOP	0x0001		/* Set to cease iteration */ | ||||
| #define AFS_VL_CURSOR_RETRY	0x0002		/* Set to do a retry */ | ||||
| #define AFS_VL_CURSOR_RETRIED	0x0004		/* Set if started a retry */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cursor for iterating over a set of fileservers. | ||||
|  */ | ||||
|  | @ -662,12 +721,12 @@ extern struct afs_addr_list *afs_alloc_addrlist(unsigned int, | |||
| 						unsigned short, | ||||
| 						unsigned short); | ||||
| extern void afs_put_addrlist(struct afs_addr_list *); | ||||
| extern struct afs_addr_list *afs_parse_text_addrs(const char *, size_t, char, | ||||
| 						  unsigned short, unsigned short); | ||||
| extern struct afs_addr_list *afs_dns_query(struct afs_cell *, time64_t *); | ||||
| extern struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *, | ||||
| 						      const char *, size_t, char, | ||||
| 						      unsigned short, unsigned short); | ||||
| extern struct afs_vlserver_list *afs_dns_query(struct afs_cell *, time64_t *); | ||||
| extern bool afs_iterate_addresses(struct afs_addr_cursor *); | ||||
| extern int afs_end_cursor(struct afs_addr_cursor *); | ||||
| extern int afs_set_vl_cursor(struct afs_addr_cursor *, struct afs_cell *); | ||||
| 
 | ||||
| extern void afs_merge_fs_addr4(struct afs_addr_list *, __be32, u16); | ||||
| extern void afs_merge_fs_addr6(struct afs_addr_list *, __be32 *, u16); | ||||
|  | @ -1088,14 +1147,43 @@ extern void afs_fs_exit(void); | |||
| /*
 | ||||
|  * vlclient.c | ||||
|  */ | ||||
| extern struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_net *, | ||||
| 							 struct afs_addr_cursor *, | ||||
| 							 struct key *, const char *, int); | ||||
| extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *, struct afs_addr_cursor *, | ||||
| 						struct key *, const uuid_t *); | ||||
| extern struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *, | ||||
| 							 const char *, int); | ||||
| extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *, const uuid_t *); | ||||
| extern int afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *, struct key *); | ||||
| extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_net *, struct afs_addr_cursor *, | ||||
| 						     struct key *, const uuid_t *); | ||||
| extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *); | ||||
| 
 | ||||
| /*
 | ||||
|  * vl_rotate.c | ||||
|  */ | ||||
| extern bool afs_begin_vlserver_operation(struct afs_vl_cursor *, | ||||
| 					 struct afs_cell *, struct key *); | ||||
| extern bool afs_select_vlserver(struct afs_vl_cursor *); | ||||
| extern bool afs_select_current_vlserver(struct afs_vl_cursor *); | ||||
| extern int afs_end_vlserver_operation(struct afs_vl_cursor *); | ||||
| 
 | ||||
| /*
 | ||||
|  * vlserver_list.c | ||||
|  */ | ||||
| static inline struct afs_vlserver *afs_get_vlserver(struct afs_vlserver *vlserver) | ||||
| { | ||||
| 	atomic_inc(&vlserver->usage); | ||||
| 	return vlserver; | ||||
| } | ||||
| 
 | ||||
| static inline struct afs_vlserver_list *afs_get_vlserverlist(struct afs_vlserver_list *vllist) | ||||
| { | ||||
| 	if (vllist) | ||||
| 		atomic_inc(&vllist->usage); | ||||
| 	return vllist; | ||||
| } | ||||
| 
 | ||||
| extern struct afs_vlserver *afs_alloc_vlserver(const char *, size_t, unsigned short); | ||||
| extern void afs_put_vlserver(struct afs_net *, struct afs_vlserver *); | ||||
| extern struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int); | ||||
| extern void afs_put_vlserverlist(struct afs_net *, struct afs_vlserver_list *); | ||||
| extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *, | ||||
| 							   const void *, size_t); | ||||
| 
 | ||||
| /*
 | ||||
|  * volume.c | ||||
|  |  | |||
|  | @ -17,6 +17,11 @@ | |||
| #include <linux/uaccess.h> | ||||
| #include "internal.h" | ||||
| 
 | ||||
| struct afs_vl_seq_net_private { | ||||
| 	struct seq_net_private		seq;	/* Must be first */ | ||||
| 	struct afs_vlserver_list	*vllist; | ||||
| }; | ||||
| 
 | ||||
| static inline struct afs_net *afs_seq2net(struct seq_file *m) | ||||
| { | ||||
| 	return afs_net(seq_file_net(m)); | ||||
|  | @ -247,61 +252,102 @@ static const struct seq_operations afs_proc_cell_volumes_ops = { | |||
| 	.show	= afs_proc_cell_volumes_show, | ||||
| }; | ||||
| 
 | ||||
| static const char *const dns_record_sources[NR__dns_record_source + 1] = { | ||||
| 	[DNS_RECORD_UNAVAILABLE]	= "unav", | ||||
| 	[DNS_RECORD_FROM_CONFIG]	= "cfg", | ||||
| 	[DNS_RECORD_FROM_DNS_A]		= "A", | ||||
| 	[DNS_RECORD_FROM_DNS_AFSDB]	= "AFSDB", | ||||
| 	[DNS_RECORD_FROM_DNS_SRV]	= "SRV", | ||||
| 	[DNS_RECORD_FROM_NSS]		= "nss", | ||||
| 	[NR__dns_record_source]		= "[weird]" | ||||
| }; | ||||
| 
 | ||||
| static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = { | ||||
| 	[DNS_LOOKUP_NOT_DONE]		= "no-lookup", | ||||
| 	[DNS_LOOKUP_GOOD]		= "good", | ||||
| 	[DNS_LOOKUP_GOOD_WITH_BAD]	= "good/bad", | ||||
| 	[DNS_LOOKUP_BAD]		= "bad", | ||||
| 	[DNS_LOOKUP_GOT_NOT_FOUND]	= "not-found", | ||||
| 	[DNS_LOOKUP_GOT_LOCAL_FAILURE]	= "local-failure", | ||||
| 	[DNS_LOOKUP_GOT_TEMP_FAILURE]	= "temp-failure", | ||||
| 	[DNS_LOOKUP_GOT_NS_FAILURE]	= "ns-failure", | ||||
| 	[NR__dns_lookup_status]		= "[weird]" | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Display the list of Volume Location servers we're using for a cell. | ||||
|  */ | ||||
| static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) | ||||
| { | ||||
| 	struct sockaddr_rxrpc *addr = v; | ||||
| 	const struct afs_vl_seq_net_private *priv = m->private; | ||||
| 	const struct afs_vlserver_list *vllist = priv->vllist; | ||||
| 	const struct afs_vlserver_entry *entry; | ||||
| 	const struct afs_vlserver *vlserver; | ||||
| 	const struct afs_addr_list *alist; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* display header on line 1 */ | ||||
| 	if (v == (void *)1) { | ||||
| 		seq_puts(m, "ADDRESS\n"); | ||||
| 	if (v == SEQ_START_TOKEN) { | ||||
| 		seq_printf(m, "# source %s, status %s\n", | ||||
| 			   dns_record_sources[vllist->source], | ||||
| 			   dns_lookup_statuses[vllist->status]); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* display one cell per line on subsequent lines */ | ||||
| 	seq_printf(m, "%pISp\n", &addr->transport); | ||||
| 	entry = v; | ||||
| 	vlserver = entry->server; | ||||
| 	alist = rcu_dereference(vlserver->addresses); | ||||
| 
 | ||||
| 	seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n", | ||||
| 		   vlserver->name, entry->priority, entry->weight, | ||||
| 		   dns_record_sources[alist ? alist->source : entry->source], | ||||
| 		   dns_lookup_statuses[alist ? alist->status : entry->status]); | ||||
| 	if (alist) { | ||||
| 		for (i = 0; i < alist->nr_addrs; i++) | ||||
| 			seq_printf(m, " %c %pISpc\n", | ||||
| 				   alist->index == i ? '>' : '-', | ||||
| 				   &alist->addrs[i].transport); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) | ||||
| 	__acquires(rcu) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	struct afs_vl_seq_net_private *priv = m->private; | ||||
| 	struct afs_vlserver_list *vllist; | ||||
| 	struct afs_cell *cell = PDE_DATA(file_inode(m->file)); | ||||
| 	loff_t pos = *_pos; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 
 | ||||
| 	alist = rcu_dereference(cell->vl_addrs); | ||||
| 	vllist = rcu_dereference(cell->vl_servers); | ||||
| 	priv->vllist = vllist; | ||||
| 
 | ||||
| 	/* allow for the header line */ | ||||
| 	if (!pos) | ||||
| 		return (void *) 1; | ||||
| 	pos--; | ||||
| 	if (pos < 0) | ||||
| 		*_pos = pos = 0; | ||||
| 	if (pos == 0) | ||||
| 		return SEQ_START_TOKEN; | ||||
| 
 | ||||
| 	if (!alist || pos >= alist->nr_addrs) | ||||
| 	if (!vllist || pos - 1 >= vllist->nr_servers) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return alist->addrs + pos; | ||||
| 	return &vllist->servers[pos - 1]; | ||||
| } | ||||
| 
 | ||||
| static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v, | ||||
| 					  loff_t *_pos) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	struct afs_cell *cell = PDE_DATA(file_inode(m->file)); | ||||
| 	struct afs_vl_seq_net_private *priv = m->private; | ||||
| 	struct afs_vlserver_list *vllist = priv->vllist; | ||||
| 	loff_t pos; | ||||
| 
 | ||||
| 	alist = rcu_dereference(cell->vl_addrs); | ||||
| 
 | ||||
| 	pos = *_pos; | ||||
| 	(*_pos)++; | ||||
| 	if (!alist || pos >= alist->nr_addrs) | ||||
| 	pos++; | ||||
| 	*_pos = pos; | ||||
| 	if (!vllist || pos - 1 >= vllist->nr_servers) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return alist->addrs + pos; | ||||
| 	return &vllist->servers[pos - 1]; | ||||
| } | ||||
| 
 | ||||
| static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v) | ||||
|  | @ -562,7 +608,7 @@ int afs_proc_cell_setup(struct afs_cell *cell) | |||
| 
 | ||||
| 	if (!proc_create_net_data("vlservers", 0444, dir, | ||||
| 				  &afs_proc_cell_vlservers_ops, | ||||
| 				  sizeof(struct seq_net_private), | ||||
| 				  sizeof(struct afs_vl_seq_net_private), | ||||
| 				  cell) || | ||||
| 	    !proc_create_net_data("volumes", 0444, dir, | ||||
| 				  &afs_proc_cell_volumes_ops, | ||||
|  |  | |||
|  | @ -246,41 +246,23 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, | |||
| static struct afs_addr_list *afs_vl_lookup_addrs(struct afs_cell *cell, | ||||
| 						 struct key *key, const uuid_t *uuid) | ||||
| { | ||||
| 	struct afs_addr_cursor ac; | ||||
| 	struct afs_addr_list *alist; | ||||
| 	struct afs_vl_cursor vc; | ||||
| 	struct afs_addr_list *alist = NULL; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = afs_set_vl_cursor(&ac, cell); | ||||
| 	if (ret < 0) | ||||
| 		return ERR_PTR(ret); | ||||
| 
 | ||||
| 	while (afs_iterate_addresses(&ac)) { | ||||
| 		if (test_bit(ac.index, &ac.alist->yfs)) | ||||
| 			alist = afs_yfsvl_get_endpoints(cell->net, &ac, key, uuid); | ||||
| 		else | ||||
| 			alist = afs_vl_get_addrs_u(cell->net, &ac, key, uuid); | ||||
| 		switch (ac.error) { | ||||
| 		case 0: | ||||
| 			afs_end_cursor(&ac); | ||||
| 			return alist; | ||||
| 		case -ECONNABORTED: | ||||
| 			ac.error = afs_abort_to_error(ac.abort_code); | ||||
| 			goto error; | ||||
| 		case -ENOMEM: | ||||
| 		case -ENONET: | ||||
| 			goto error; | ||||
| 		case -ENETUNREACH: | ||||
| 		case -EHOSTUNREACH: | ||||
| 		case -ECONNREFUSED: | ||||
| 			break; | ||||
| 		default: | ||||
| 			ac.error = -EIO; | ||||
| 			goto error; | ||||
| 	ret = -ERESTARTSYS; | ||||
| 	if (afs_begin_vlserver_operation(&vc, cell, key)) { | ||||
| 		while (afs_select_vlserver(&vc)) { | ||||
| 			if (test_bit(vc.ac.index, &vc.ac.alist->yfs)) | ||||
| 				alist = afs_yfsvl_get_endpoints(&vc, uuid); | ||||
| 			else | ||||
| 				alist = afs_vl_get_addrs_u(&vc, uuid); | ||||
| 		} | ||||
| 
 | ||||
| 		ret = afs_end_vlserver_operation(&vc); | ||||
| 	} | ||||
| 
 | ||||
| error: | ||||
| 	return ERR_PTR(afs_end_cursor(&ac)); | ||||
| 	return ret < 0 ? ERR_PTR(ret) : alist; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
							
								
								
									
										336
									
								
								fs/afs/vl_list.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								fs/afs/vl_list.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,336 @@ | |||
| /* AFS vlserver list management.
 | ||||
|  * | ||||
|  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. | ||||
|  * Written by David Howells (dhowells@redhat.com) | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * as published by the Free Software Foundation; either version | ||||
|  * 2 of the License, or (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/slab.h> | ||||
| #include "internal.h" | ||||
| 
 | ||||
| struct afs_vlserver *afs_alloc_vlserver(const char *name, size_t name_len, | ||||
| 					unsigned short port) | ||||
| { | ||||
| 	struct afs_vlserver *vlserver; | ||||
| 
 | ||||
| 	vlserver = kzalloc(struct_size(vlserver, name, name_len + 1), | ||||
| 			   GFP_KERNEL); | ||||
| 	if (vlserver) { | ||||
| 		atomic_set(&vlserver->usage, 1); | ||||
| 		rwlock_init(&vlserver->lock); | ||||
| 		vlserver->name_len = name_len; | ||||
| 		vlserver->port = port; | ||||
| 		memcpy(vlserver->name, name, name_len); | ||||
| 	} | ||||
| 	return vlserver; | ||||
| } | ||||
| 
 | ||||
| static void afs_vlserver_rcu(struct rcu_head *rcu) | ||||
| { | ||||
| 	struct afs_vlserver *vlserver = container_of(rcu, struct afs_vlserver, rcu); | ||||
| 
 | ||||
| 	afs_put_addrlist(rcu_access_pointer(vlserver->addresses)); | ||||
| 	kfree_rcu(vlserver, rcu); | ||||
| } | ||||
| 
 | ||||
| void afs_put_vlserver(struct afs_net *net, struct afs_vlserver *vlserver) | ||||
| { | ||||
| 	if (vlserver) { | ||||
| 		unsigned int u = atomic_dec_return(&vlserver->usage); | ||||
| 		//_debug("VL PUT %p{%u}", vlserver, u);
 | ||||
| 
 | ||||
| 		if (u == 0) | ||||
| 			call_rcu(&vlserver->rcu, afs_vlserver_rcu); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers) | ||||
| { | ||||
| 	struct afs_vlserver_list *vllist; | ||||
| 
 | ||||
| 	vllist = kzalloc(struct_size(vllist, servers, nr_servers), GFP_KERNEL); | ||||
| 	if (vllist) { | ||||
| 		atomic_set(&vllist->usage, 1); | ||||
| 		rwlock_init(&vllist->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	return vllist; | ||||
| } | ||||
| 
 | ||||
| void afs_put_vlserverlist(struct afs_net *net, struct afs_vlserver_list *vllist) | ||||
| { | ||||
| 	if (vllist) { | ||||
| 		unsigned int u = atomic_dec_return(&vllist->usage); | ||||
| 
 | ||||
| 		//_debug("VLLS PUT %p{%u}", vllist, u);
 | ||||
| 		if (u == 0) { | ||||
| 			int i; | ||||
| 
 | ||||
| 			for (i = 0; i < vllist->nr_servers; i++) { | ||||
| 				afs_put_vlserver(net, vllist->servers[i].server); | ||||
| 			} | ||||
| 			kfree_rcu(vllist, rcu); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static u16 afs_extract_le16(const u8 **_b) | ||||
| { | ||||
| 	u16 val; | ||||
| 
 | ||||
| 	val  = (u16)*(*_b)++ << 0; | ||||
| 	val |= (u16)*(*_b)++ << 8; | ||||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Build a VL server address list from a DNS queried server list. | ||||
|  */ | ||||
| static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end, | ||||
| 						  u8 nr_addrs, u16 port) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	const u8 *b = *_b; | ||||
| 	int ret = -EINVAL; | ||||
| 
 | ||||
| 	alist = afs_alloc_addrlist(nr_addrs, VL_SERVICE, port); | ||||
| 	if (!alist) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	if (nr_addrs == 0) | ||||
| 		return alist; | ||||
| 
 | ||||
| 	for (; nr_addrs > 0 && end - b >= nr_addrs; nr_addrs--) { | ||||
| 		struct dns_server_list_v1_address hdr; | ||||
| 		__be32 x[4]; | ||||
| 
 | ||||
| 		hdr.address_type = *b++; | ||||
| 
 | ||||
| 		switch (hdr.address_type) { | ||||
| 		case DNS_ADDRESS_IS_IPV4: | ||||
| 			if (end - b < 4) { | ||||
| 				_leave(" = -EINVAL [short inet]"); | ||||
| 				goto error; | ||||
| 			} | ||||
| 			memcpy(x, b, 4); | ||||
| 			afs_merge_fs_addr4(alist, x[0], port); | ||||
| 			b += 4; | ||||
| 			break; | ||||
| 
 | ||||
| 		case DNS_ADDRESS_IS_IPV6: | ||||
| 			if (end - b < 16) { | ||||
| 				_leave(" = -EINVAL [short inet6]"); | ||||
| 				goto error; | ||||
| 			} | ||||
| 			memcpy(x, b, 16); | ||||
| 			afs_merge_fs_addr6(alist, x, port); | ||||
| 			b += 16; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			_leave(" = -EADDRNOTAVAIL [unknown af %u]", | ||||
| 			       hdr.address_type); | ||||
| 			ret = -EADDRNOTAVAIL; | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Start with IPv6 if available. */ | ||||
| 	if (alist->nr_ipv4 < alist->nr_addrs) | ||||
| 		alist->index = alist->nr_ipv4; | ||||
| 
 | ||||
| 	*_b = b; | ||||
| 	return alist; | ||||
| 
 | ||||
| error: | ||||
| 	*_b = b; | ||||
| 	afs_put_addrlist(alist); | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Build a VL server list from a DNS queried server list. | ||||
|  */ | ||||
| struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, | ||||
| 						    const void *buffer, | ||||
| 						    size_t buffer_size) | ||||
| { | ||||
| 	const struct dns_server_list_v1_header *hdr = buffer; | ||||
| 	struct dns_server_list_v1_server bs; | ||||
| 	struct afs_vlserver_list *vllist, *previous; | ||||
| 	struct afs_addr_list *addrs; | ||||
| 	struct afs_vlserver *server; | ||||
| 	const u8 *b = buffer, *end = buffer + buffer_size; | ||||
| 	int ret = -ENOMEM, nr_servers, i, j; | ||||
| 
 | ||||
| 	_enter(""); | ||||
| 
 | ||||
| 	/* Check that it's a server list, v1 */ | ||||
| 	if (end - b < sizeof(*hdr) || | ||||
| 	    hdr->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST || | ||||
| 	    hdr->hdr.version != 1) { | ||||
| 		pr_notice("kAFS: Got DNS record [%u,%u] len %zu\n", | ||||
| 			  hdr->hdr.content, hdr->hdr.version, end - b); | ||||
| 		ret = -EDESTADDRREQ; | ||||
| 		goto dump; | ||||
| 	} | ||||
| 
 | ||||
| 	nr_servers = hdr->nr_servers; | ||||
| 
 | ||||
| 	vllist = afs_alloc_vlserver_list(nr_servers); | ||||
| 	if (!vllist) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	vllist->source = (hdr->source < NR__dns_record_source) ? | ||||
| 		hdr->source : NR__dns_record_source; | ||||
| 	vllist->status = (hdr->status < NR__dns_lookup_status) ? | ||||
| 		hdr->status : NR__dns_lookup_status; | ||||
| 
 | ||||
| 	read_lock(&cell->vl_servers_lock); | ||||
| 	previous = afs_get_vlserverlist( | ||||
| 		rcu_dereference_protected(cell->vl_servers, | ||||
| 					  lockdep_is_held(&cell->vl_servers_lock))); | ||||
| 	read_unlock(&cell->vl_servers_lock); | ||||
| 
 | ||||
| 	b += sizeof(*hdr); | ||||
| 	while (end - b >= sizeof(bs)) { | ||||
| 		bs.name_len	= afs_extract_le16(&b); | ||||
| 		bs.priority	= afs_extract_le16(&b); | ||||
| 		bs.weight	= afs_extract_le16(&b); | ||||
| 		bs.port		= afs_extract_le16(&b); | ||||
| 		bs.source	= *b++; | ||||
| 		bs.status	= *b++; | ||||
| 		bs.protocol	= *b++; | ||||
| 		bs.nr_addrs	= *b++; | ||||
| 
 | ||||
| 		_debug("extract %u %u %u %u %u %u %*.*s", | ||||
| 		       bs.name_len, bs.priority, bs.weight, | ||||
| 		       bs.port, bs.protocol, bs.nr_addrs, | ||||
| 		       bs.name_len, bs.name_len, b); | ||||
| 
 | ||||
| 		if (end - b < bs.name_len) | ||||
| 			break; | ||||
| 
 | ||||
| 		ret = -EPROTONOSUPPORT; | ||||
| 		if (bs.protocol == DNS_SERVER_PROTOCOL_UNSPECIFIED) { | ||||
| 			bs.protocol = DNS_SERVER_PROTOCOL_UDP; | ||||
| 		} else if (bs.protocol != DNS_SERVER_PROTOCOL_UDP) { | ||||
| 			_leave(" = [proto %u]", bs.protocol); | ||||
| 			goto error; | ||||
| 		} | ||||
| 
 | ||||
| 		if (bs.port == 0) | ||||
| 			bs.port = AFS_VL_PORT; | ||||
| 		if (bs.source > NR__dns_record_source) | ||||
| 			bs.source = NR__dns_record_source; | ||||
| 		if (bs.status > NR__dns_lookup_status) | ||||
| 			bs.status = NR__dns_lookup_status; | ||||
| 
 | ||||
| 		server = NULL; | ||||
| 		if (previous) { | ||||
| 			/* See if we can update an old server record */ | ||||
| 			for (i = 0; i < previous->nr_servers; i++) { | ||||
| 				struct afs_vlserver *p = previous->servers[i].server; | ||||
| 
 | ||||
| 				if (p->name_len == bs.name_len && | ||||
| 				    p->port == bs.port && | ||||
| 				    strncasecmp(b, p->name, bs.name_len) == 0) { | ||||
| 					server = afs_get_vlserver(p); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!server) { | ||||
| 			ret = -ENOMEM; | ||||
| 			server = afs_alloc_vlserver(b, bs.name_len, bs.port); | ||||
| 			if (!server) | ||||
| 				goto error; | ||||
| 		} | ||||
| 
 | ||||
| 		b += bs.name_len; | ||||
| 
 | ||||
| 		/* Extract the addresses - note that we can't skip this as we
 | ||||
| 		 * have to advance the payload pointer. | ||||
| 		 */ | ||||
| 		addrs = afs_extract_vl_addrs(&b, end, bs.nr_addrs, bs.port); | ||||
| 		if (IS_ERR(addrs)) { | ||||
| 			ret = PTR_ERR(addrs); | ||||
| 			goto error_2; | ||||
| 		} | ||||
| 
 | ||||
| 		if (vllist->nr_servers >= nr_servers) { | ||||
| 			_debug("skip %u >= %u", vllist->nr_servers, nr_servers); | ||||
| 			afs_put_addrlist(addrs); | ||||
| 			afs_put_vlserver(cell->net, server); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		addrs->source = bs.source; | ||||
| 		addrs->status = bs.status; | ||||
| 
 | ||||
| 		if (addrs->nr_addrs == 0) { | ||||
| 			afs_put_addrlist(addrs); | ||||
| 			if (!rcu_access_pointer(server->addresses)) { | ||||
| 				afs_put_vlserver(cell->net, server); | ||||
| 				continue; | ||||
| 			} | ||||
| 		} else { | ||||
| 			struct afs_addr_list *old = addrs; | ||||
| 
 | ||||
| 			write_lock(&server->lock); | ||||
| 			rcu_swap_protected(server->addresses, old, | ||||
| 					   lockdep_is_held(&server->lock)); | ||||
| 			write_unlock(&server->lock); | ||||
| 			afs_put_addrlist(old); | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 		/* TODO: Might want to check for duplicates */ | ||||
| 
 | ||||
| 		/* Insertion-sort by priority and weight */ | ||||
| 		for (j = 0; j < vllist->nr_servers; j++) { | ||||
| 			if (bs.priority < vllist->servers[j].priority) | ||||
| 				break; /* Lower preferable */ | ||||
| 			if (bs.priority == vllist->servers[j].priority && | ||||
| 			    bs.weight > vllist->servers[j].weight) | ||||
| 				break; /* Higher preferable */ | ||||
| 		} | ||||
| 
 | ||||
| 		if (j < vllist->nr_servers) { | ||||
| 			memmove(vllist->servers + j + 1, | ||||
| 				vllist->servers + j, | ||||
| 				(vllist->nr_servers - j) * sizeof(struct afs_vlserver_entry)); | ||||
| 		} | ||||
| 
 | ||||
| 		vllist->servers[j].priority = bs.priority; | ||||
| 		vllist->servers[j].weight = bs.weight; | ||||
| 		vllist->servers[j].server = server; | ||||
| 		vllist->nr_servers++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (b != end) { | ||||
| 		_debug("parse error %zd", b - end); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	afs_put_vlserverlist(cell->net, previous); | ||||
| 	_leave(" = ok [%u]", vllist->nr_servers); | ||||
| 	return vllist; | ||||
| 
 | ||||
| error_2: | ||||
| 	afs_put_vlserver(cell->net, server); | ||||
| error: | ||||
| 	afs_put_vlserverlist(cell->net, vllist); | ||||
| 	afs_put_vlserverlist(cell->net, previous); | ||||
| dump: | ||||
| 	if (ret != -ENOMEM) { | ||||
| 		printk(KERN_DEBUG "DNS: at %zu\n", (const void *)b - buffer); | ||||
| 		print_hex_dump_bytes("DNS: ", DUMP_PREFIX_NONE, buffer, buffer_size); | ||||
| 	} | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
							
								
								
									
										251
									
								
								fs/afs/vl_rotate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								fs/afs/vl_rotate.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,251 @@ | |||
| /* Handle vlserver selection and rotation.
 | ||||
|  * | ||||
|  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. | ||||
|  * Written by David Howells (dhowells@redhat.com) | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public Licence | ||||
|  * as published by the Free Software Foundation; either version | ||||
|  * 2 of the Licence, or (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/sched/signal.h> | ||||
| #include "internal.h" | ||||
| #include "afs_vl.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * Begin an operation on a volume location server. | ||||
|  */ | ||||
| bool afs_begin_vlserver_operation(struct afs_vl_cursor *vc, struct afs_cell *cell, | ||||
| 				  struct key *key) | ||||
| { | ||||
| 	memset(vc, 0, sizeof(*vc)); | ||||
| 	vc->cell = cell; | ||||
| 	vc->key = key; | ||||
| 	vc->error = -EDESTADDRREQ; | ||||
| 	vc->ac.error = SHRT_MAX; | ||||
| 
 | ||||
| 	if (signal_pending(current)) { | ||||
| 		vc->error = -EINTR; | ||||
| 		vc->flags |= AFS_VL_CURSOR_STOP; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Begin iteration through a server list, starting with the last used server if | ||||
|  * possible, or the last recorded good server if not. | ||||
|  */ | ||||
| static bool afs_start_vl_iteration(struct afs_vl_cursor *vc) | ||||
| { | ||||
| 	struct afs_cell *cell = vc->cell; | ||||
| 
 | ||||
| 	if (wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET, | ||||
| 			TASK_INTERRUPTIBLE)) { | ||||
| 		vc->error = -ERESTARTSYS; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	read_lock(&cell->vl_servers_lock); | ||||
| 	vc->server_list = afs_get_vlserverlist( | ||||
| 		rcu_dereference_protected(cell->vl_servers, | ||||
| 					  lockdep_is_held(&cell->vl_servers_lock))); | ||||
| 	read_unlock(&cell->vl_servers_lock); | ||||
| 	if (!vc->server_list || !vc->server_list->nr_servers) | ||||
| 		return false; | ||||
| 
 | ||||
| 	vc->start = READ_ONCE(vc->server_list->index); | ||||
| 	vc->index = vc->start; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Select the vlserver to use.  May be called multiple times to rotate | ||||
|  * through the vlservers. | ||||
|  */ | ||||
| bool afs_select_vlserver(struct afs_vl_cursor *vc) | ||||
| { | ||||
| 	struct afs_addr_list *alist; | ||||
| 	struct afs_vlserver *vlserver; | ||||
| 	int error = vc->ac.error; | ||||
| 
 | ||||
| 	_enter("%u/%u,%u/%u,%d,%d", | ||||
| 	       vc->index, vc->start, | ||||
| 	       vc->ac.index, vc->ac.start, | ||||
| 	       error, vc->ac.abort_code); | ||||
| 
 | ||||
| 	if (vc->flags & AFS_VL_CURSOR_STOP) { | ||||
| 		_leave(" = f [stopped]"); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Evaluate the result of the previous operation, if there was one. */ | ||||
| 	switch (error) { | ||||
| 	case SHRT_MAX: | ||||
| 		goto start; | ||||
| 
 | ||||
| 	default: | ||||
| 	case 0: | ||||
| 		/* Success or local failure.  Stop. */ | ||||
| 		vc->error = error; | ||||
| 		vc->flags |= AFS_VL_CURSOR_STOP; | ||||
| 		_leave(" = f [okay/local %d]", vc->ac.error); | ||||
| 		return false; | ||||
| 
 | ||||
| 	case -ECONNABORTED: | ||||
| 		/* The far side rejected the operation on some grounds.  This
 | ||||
| 		 * might involve the server being busy or the volume having been moved. | ||||
| 		 */ | ||||
| 		switch (vc->ac.abort_code) { | ||||
| 		case AFSVL_IO: | ||||
| 		case AFSVL_BADVOLOPER: | ||||
| 		case AFSVL_NOMEM: | ||||
| 			/* The server went weird. */ | ||||
| 			vc->error = -EREMOTEIO; | ||||
| 			//write_lock(&vc->cell->vl_servers_lock);
 | ||||
| 			//vc->server_list->weird_mask |= 1 << vc->index;
 | ||||
| 			//write_unlock(&vc->cell->vl_servers_lock);
 | ||||
| 			goto next_server; | ||||
| 
 | ||||
| 		default: | ||||
| 			vc->error = afs_abort_to_error(vc->ac.abort_code); | ||||
| 			goto failed; | ||||
| 		} | ||||
| 
 | ||||
| 	case -ENETUNREACH: | ||||
| 	case -EHOSTUNREACH: | ||||
| 	case -ECONNREFUSED: | ||||
| 	case -ETIMEDOUT: | ||||
| 	case -ETIME: | ||||
| 		_debug("no conn %d", error); | ||||
| 		vc->error = error; | ||||
| 		goto iterate_address; | ||||
| 
 | ||||
| 	case -ECONNRESET: | ||||
| 		_debug("call reset"); | ||||
| 		vc->error = error; | ||||
| 		vc->flags |= AFS_VL_CURSOR_RETRY; | ||||
| 		goto next_server; | ||||
| 	} | ||||
| 
 | ||||
| restart_from_beginning: | ||||
| 	_debug("restart"); | ||||
| 	afs_end_cursor(&vc->ac); | ||||
| 	afs_put_vlserverlist(vc->cell->net, vc->server_list); | ||||
| 	vc->server_list = NULL; | ||||
| 	if (vc->flags & AFS_VL_CURSOR_RETRIED) | ||||
| 		goto failed; | ||||
| 	vc->flags |= AFS_VL_CURSOR_RETRIED; | ||||
| start: | ||||
| 	_debug("start"); | ||||
| 
 | ||||
| 	/* TODO: Consider checking the VL server list */ | ||||
| 
 | ||||
| 	if (!afs_start_vl_iteration(vc)) | ||||
| 		goto failed; | ||||
| 
 | ||||
| use_server: | ||||
| 	_debug("use"); | ||||
| 	/* We're starting on a different vlserver from the list.  We need to
 | ||||
| 	 * check it, find its address list and probe its capabilities before we | ||||
| 	 * use it. | ||||
| 	 */ | ||||
| 	ASSERTCMP(vc->ac.alist, ==, NULL); | ||||
| 	vlserver = vc->server_list->servers[vc->index].server; | ||||
| 
 | ||||
| 	// TODO: Check the vlserver occasionally
 | ||||
| 	//if (!afs_check_vlserver_record(vc, vlserver))
 | ||||
| 	//	goto failed;
 | ||||
| 
 | ||||
| 	_debug("USING VLSERVER: %s", vlserver->name); | ||||
| 
 | ||||
| 	read_lock(&vlserver->lock); | ||||
| 	alist = rcu_dereference_protected(vlserver->addresses, | ||||
| 					  lockdep_is_held(&vlserver->lock)); | ||||
| 	afs_get_addrlist(alist); | ||||
| 	read_unlock(&vlserver->lock); | ||||
| 
 | ||||
| 	memset(&vc->ac, 0, sizeof(vc->ac)); | ||||
| 
 | ||||
| 	/* Probe the current vlserver if we haven't done so yet. */ | ||||
| #if 0 // TODO
 | ||||
| 	if (!test_bit(AFS_VLSERVER_FL_PROBED, &vlserver->flags)) { | ||||
| 		vc->ac.alist = afs_get_addrlist(alist); | ||||
| 
 | ||||
| 		if (!afs_probe_vlserver(vc)) { | ||||
| 			error = vc->ac.error; | ||||
| 			switch (error) { | ||||
| 			case -ENOMEM: | ||||
| 			case -ERESTARTSYS: | ||||
| 			case -EINTR: | ||||
| 				goto failed_set_error; | ||||
| 			default: | ||||
| 				goto next_server; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	if (!vc->ac.alist) | ||||
| 		vc->ac.alist = alist; | ||||
| 	else | ||||
| 		afs_put_addrlist(alist); | ||||
| 
 | ||||
| 	vc->ac.start = READ_ONCE(alist->index); | ||||
| 	vc->ac.index = vc->ac.start; | ||||
| 
 | ||||
| iterate_address: | ||||
| 	ASSERT(vc->ac.alist); | ||||
| 	_debug("iterate %d/%d", vc->ac.index, vc->ac.alist->nr_addrs); | ||||
| 	/* Iterate over the current server's address list to try and find an
 | ||||
| 	 * address on which it will respond to us. | ||||
| 	 */ | ||||
| 	if (!afs_iterate_addresses(&vc->ac)) | ||||
| 		goto next_server; | ||||
| 
 | ||||
| 	_leave(" = t %pISpc", &vc->ac.addr->transport); | ||||
| 	return true; | ||||
| 
 | ||||
| next_server: | ||||
| 	_debug("next"); | ||||
| 	afs_end_cursor(&vc->ac); | ||||
| 	vc->index++; | ||||
| 	if (vc->index >= vc->server_list->nr_servers) | ||||
| 		vc->index = 0; | ||||
| 	if (vc->index != vc->start) | ||||
| 		goto use_server; | ||||
| 
 | ||||
| 	/* That's all the servers poked to no good effect.  Try again if some
 | ||||
| 	 * of them were busy. | ||||
| 	 */ | ||||
| 	if (vc->flags & AFS_VL_CURSOR_RETRY) | ||||
| 		goto restart_from_beginning; | ||||
| 
 | ||||
| 	goto failed; | ||||
| 
 | ||||
| failed: | ||||
| 	vc->flags |= AFS_VL_CURSOR_STOP; | ||||
| 	afs_end_cursor(&vc->ac); | ||||
| 	_leave(" = f [failed %d]", vc->error); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Tidy up a volume location server cursor and unlock the vnode. | ||||
|  */ | ||||
| int afs_end_vlserver_operation(struct afs_vl_cursor *vc) | ||||
| { | ||||
| 	struct afs_net *net = vc->cell->net; | ||||
| 
 | ||||
| 	afs_end_cursor(&vc->ac); | ||||
| 	afs_put_vlserverlist(net, vc->server_list); | ||||
| 
 | ||||
| 	if (vc->error == -ECONNABORTED) | ||||
| 		vc->error = afs_abort_to_error(vc->ac.abort_code); | ||||
| 
 | ||||
| 	return vc->error; | ||||
| } | ||||
|  | @ -128,14 +128,13 @@ static const struct afs_call_type afs_RXVLGetEntryByNameU = { | |||
|  * Dispatch a get volume entry by name or ID operation (uuid variant).  If the | ||||
|  * volname is a decimal number then it's a volume ID not a volume name. | ||||
|  */ | ||||
| struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_net *net, | ||||
| 						  struct afs_addr_cursor *ac, | ||||
| 						  struct key *key, | ||||
| struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc, | ||||
| 						  const char *volname, | ||||
| 						  int volnamesz) | ||||
| { | ||||
| 	struct afs_vldb_entry *entry; | ||||
| 	struct afs_call *call; | ||||
| 	struct afs_net *net = vc->cell->net; | ||||
| 	size_t reqsz, padsz; | ||||
| 	__be32 *bp; | ||||
| 
 | ||||
|  | @ -155,7 +154,7 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_net *net, | |||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	} | ||||
| 
 | ||||
| 	call->key = key; | ||||
| 	call->key = vc->key; | ||||
| 	call->reply[0] = entry; | ||||
| 	call->ret_reply0 = true; | ||||
| 
 | ||||
|  | @ -168,7 +167,7 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_net *net, | |||
| 		memset((void *)bp + volnamesz, 0, padsz); | ||||
| 
 | ||||
| 	trace_afs_make_vl_call(call); | ||||
| 	return (struct afs_vldb_entry *)afs_make_call(ac, call, GFP_KERNEL, false); | ||||
| 	return (struct afs_vldb_entry *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -266,14 +265,13 @@ static const struct afs_call_type afs_RXVLGetAddrsU = { | |||
|  * Dispatch an operation to get the addresses for a server, where the server is | ||||
|  * nominated by UUID. | ||||
|  */ | ||||
| struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *net, | ||||
| 					 struct afs_addr_cursor *ac, | ||||
| 					 struct key *key, | ||||
| struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc, | ||||
| 					 const uuid_t *uuid) | ||||
| { | ||||
| 	struct afs_ListAddrByAttributes__xdr *r; | ||||
| 	const struct afs_uuid *u = (const struct afs_uuid *)uuid; | ||||
| 	struct afs_call *call; | ||||
| 	struct afs_net *net = vc->cell->net; | ||||
| 	__be32 *bp; | ||||
| 	int i; | ||||
| 
 | ||||
|  | @ -285,7 +283,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *net, | |||
| 	if (!call) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	call->key = key; | ||||
| 	call->key = vc->key; | ||||
| 	call->reply[0] = NULL; | ||||
| 	call->ret_reply0 = true; | ||||
| 
 | ||||
|  | @ -306,7 +304,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *net, | |||
| 		r->uuid.node[i] = htonl(u->node[i]); | ||||
| 
 | ||||
| 	trace_afs_make_vl_call(call); | ||||
| 	return (struct afs_addr_list *)afs_make_call(ac, call, GFP_KERNEL, false); | ||||
| 	return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -367,14 +365,13 @@ static const struct afs_call_type afs_RXVLGetCapabilities = { | |||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Probe a fileserver for the capabilities that it supports.  This can | ||||
|  * Probe a volume server for the capabilities that it supports.  This can | ||||
|  * return up to 196 words. | ||||
|  * | ||||
|  * 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, | ||||
| int afs_vl_get_capabilities(struct afs_net *net, struct afs_addr_cursor *ac, | ||||
| 			    struct key *key) | ||||
| { | ||||
| 	struct afs_call *call; | ||||
|  | @ -617,12 +614,11 @@ static const struct afs_call_type afs_YFSVLGetEndpoints = { | |||
|  * Dispatch an operation to get the addresses for a server, where the server is | ||||
|  * nominated by UUID. | ||||
|  */ | ||||
| struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_net *net, | ||||
| 					      struct afs_addr_cursor *ac, | ||||
| 					      struct key *key, | ||||
| struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc, | ||||
| 					      const uuid_t *uuid) | ||||
| { | ||||
| 	struct afs_call *call; | ||||
| 	struct afs_net *net = vc->cell->net; | ||||
| 	__be32 *bp; | ||||
| 
 | ||||
| 	_enter(""); | ||||
|  | @ -633,7 +629,7 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_net *net, | |||
| 	if (!call) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	call->key = key; | ||||
| 	call->key = vc->key; | ||||
| 	call->reply[0] = NULL; | ||||
| 	call->ret_reply0 = true; | ||||
| 
 | ||||
|  | @ -644,5 +640,5 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_net *net, | |||
| 	memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */ | ||||
| 
 | ||||
| 	trace_afs_make_vl_call(call); | ||||
| 	return (struct afs_addr_list *)afs_make_call(ac, call, GFP_KERNEL, false); | ||||
| 	return (struct afs_addr_list *)afs_make_call(&vc->ac, call, GFP_KERNEL, false); | ||||
| } | ||||
|  |  | |||
|  | @ -74,55 +74,35 @@ static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell, | |||
| 						 const char *volname, | ||||
| 						 size_t volnamesz) | ||||
| { | ||||
| 	struct afs_addr_cursor ac; | ||||
| 	struct afs_vldb_entry *vldb; | ||||
| 	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ); | ||||
| 	struct afs_vl_cursor vc; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = afs_set_vl_cursor(&ac, cell); | ||||
| 	if (ret < 0) | ||||
| 		return ERR_PTR(ret); | ||||
| 	if (!afs_begin_vlserver_operation(&vc, cell, key)) | ||||
| 		return ERR_PTR(-ERESTARTSYS); | ||||
| 
 | ||||
| 	while (afs_iterate_addresses(&ac)) { | ||||
| 		if (!test_bit(ac.index, &ac.alist->probed)) { | ||||
| 			ret = afs_vl_get_capabilities(cell->net, &ac, key); | ||||
| 	while (afs_select_vlserver(&vc)) { | ||||
| 		if (!test_bit(vc.ac.index, &vc.ac.alist->probed)) { | ||||
| 			ret = afs_vl_get_capabilities(cell->net, &vc.ac, key); | ||||
| 			switch (ret) { | ||||
| 			case VL_SERVICE: | ||||
| 				clear_bit(ac.index, &ac.alist->yfs); | ||||
| 				set_bit(ac.index, &ac.alist->probed); | ||||
| 				ac.addr->srx_service = ret; | ||||
| 				clear_bit(vc.ac.index, &vc.ac.alist->yfs); | ||||
| 				set_bit(vc.ac.index, &vc.ac.alist->probed); | ||||
| 				vc.ac.addr->srx_service = ret; | ||||
| 				break; | ||||
| 			case YFS_VL_SERVICE: | ||||
| 				set_bit(ac.index, &ac.alist->yfs); | ||||
| 				set_bit(ac.index, &ac.alist->probed); | ||||
| 				ac.addr->srx_service = ret; | ||||
| 				set_bit(vc.ac.index, &vc.ac.alist->yfs); | ||||
| 				set_bit(vc.ac.index, &vc.ac.alist->probed); | ||||
| 				vc.ac.addr->srx_service = ret; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		vldb = afs_vl_get_entry_by_name_u(cell->net, &ac, key, | ||||
| 						  volname, volnamesz); | ||||
| 		switch (ac.error) { | ||||
| 		case 0: | ||||
| 			afs_end_cursor(&ac); | ||||
| 			return vldb; | ||||
| 		case -ECONNABORTED: | ||||
| 			ac.error = afs_abort_to_error(ac.abort_code); | ||||
| 			goto error; | ||||
| 		case -ENOMEM: | ||||
| 		case -ENONET: | ||||
| 			goto error; | ||||
| 		case -ENETUNREACH: | ||||
| 		case -EHOSTUNREACH: | ||||
| 		case -ECONNREFUSED: | ||||
| 			break; | ||||
| 		default: | ||||
| 			ac.error = -EIO; | ||||
| 			goto error; | ||||
| 		} | ||||
| 		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz); | ||||
| 	} | ||||
| 
 | ||||
| error: | ||||
| 	return ERR_PTR(afs_end_cursor(&ac)); | ||||
| 	ret = afs_end_vlserver_operation(&vc); | ||||
| 	return ret < 0 ? ERR_PTR(ret) : vldb; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 David Howells
						David Howells