* [PATCH 0/3] Support multiple ips per hostname
@ 2021-05-11 16:36 Paulo Alcantara
2021-05-11 16:36 ` [PATCH 1/3] cifs: introduce smb3_options_for_each() helper Paulo Alcantara
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Paulo Alcantara @ 2021-05-11 16:36 UTC (permalink / raw)
To: linux-cifs, smfrench; +Cc: Paulo Alcantara
Steve,
Follow a series to handle multiple ip addresses per hostame when
mounting, chasing DFS referrals and reconnecting.
We've defined a maximum number of 16 addresses a hostname can handle
as we did in mount.cifs (resolve_host.c::resolve_host). If that's not
OK, then we could make it flexible by creating a new mount option like
'maxaddrs=N' or just increase it in both sides.
The mount.cifs(8) patch that passes multiple 'ip=' options to handle
all resolved addresses will be posted soon.
Paulo Alcantara (3):
cifs: introduce smb3_options_for_each() helper
cifs: handle multiple ip addresses per hostname
cifs: fix find_root_ses() when refresing dfs cache
fs/cifs/cifs_dfs_ref.c | 48 +-
fs/cifs/cifsglob.h | 24 +-
fs/cifs/connect.c | 1042 ++++++++++++++++++++++++----------------
fs/cifs/dfs_cache.c | 12 +-
fs/cifs/dns_resolve.c | 72 +--
fs/cifs/dns_resolve.h | 5 +-
fs/cifs/fs_context.c | 22 +-
fs/cifs/fs_context.h | 4 +-
fs/cifs/misc.c | 95 +++-
9 files changed, 828 insertions(+), 496 deletions(-)
--
2.31.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/3] cifs: introduce smb3_options_for_each() helper
2021-05-11 16:36 [PATCH 0/3] Support multiple ips per hostname Paulo Alcantara
@ 2021-05-11 16:36 ` Paulo Alcantara
2021-05-11 18:15 ` kernel test robot
2021-05-11 16:36 ` [PATCH 2/3] cifs: handle multiple ip addresses per hostname Paulo Alcantara
2021-05-11 16:36 ` [PATCH 3/3] cifs: fix find_root_ses() when refresing dfs cache Paulo Alcantara
2 siblings, 1 reply; 7+ messages in thread
From: Paulo Alcantara @ 2021-05-11 16:36 UTC (permalink / raw)
To: linux-cifs, smfrench; +Cc: Paulo Alcantara
Introduce a new helper to walk through the mount options and avoid
duplicating it all over the cifs code.
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
---
fs/cifs/cifsglob.h | 16 ++++++++++++++++
fs/cifs/misc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index ea90c53386b8..6c65e39f0509 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1960,4 +1960,20 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
}
+char *smb3_parse_option(char *options, char sep, char **nkey, char **nval);
+
+static inline char *smb3_parse_options_sep(char *options, char *sep, char **nkey, char **nval)
+{
+ *sep = ',';
+ if (!strncmp(options, "sep=", 4)) {
+ *sep = options[4];
+ options += 5;
+ }
+ return smb3_parse_option(options, *sep, nkey, nval);
+}
+
+#define smb3_options_for_each(opts, nopts, sep, key, val) \
+ for (nopts = (opts), nopts = smb3_parse_options_sep(nopts, &sep, &(key), &(val)); \
+ (key); nopts = smb3_parse_option(nopts, sep, &(key), &(val)))
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index cd705f8a4e31..7b3b1ea76baf 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1284,3 +1284,51 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
cifs_put_tcon_super(sb);
return rc;
}
+
+/**
+ * Parse a string that is in key[=val][,key[=val]]* form.
+ *
+ * @options: The options to parse
+ * @sep: The options separator
+ * @nkey: The next key pointer
+ * @nval: The next value pointer
+ *
+ * Returns next key-value pair to be parsed, otherwise NULL.
+ */
+char *smb3_parse_option(char *options, char sep, char **nkey, char **nval)
+{
+ char *key, *value;
+ char sepstr[2] = {sep, '\0'};
+
+ *nkey = NULL;
+ *nval = NULL;
+
+ if (!options || !options[0])
+ return NULL;
+
+ while ((key = strsep(&options, sepstr)) != NULL) {
+ size_t len;
+
+ if (*key == 0)
+ return NULL;
+
+ while (options && options[0] == sep) {
+ len = strlen(key);
+ strcpy(key + len, options);
+ options = strchr(options, sep);
+ if (options)
+ *options++ = 0;
+ }
+
+ value = strchr(key, '=');
+ if (value) {
+ if (value == key)
+ continue;
+ *value++ = 0;
+ }
+ break;
+ }
+ *nkey = key;
+ *nval = value;
+ return options;
+}
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/3] cifs: handle multiple ip addresses per hostname
2021-05-11 16:36 [PATCH 0/3] Support multiple ips per hostname Paulo Alcantara
2021-05-11 16:36 ` [PATCH 1/3] cifs: introduce smb3_options_for_each() helper Paulo Alcantara
@ 2021-05-11 16:36 ` Paulo Alcantara
2021-05-11 18:47 ` kernel test robot
2021-05-11 18:47 ` kernel test robot
2021-05-11 16:36 ` [PATCH 3/3] cifs: fix find_root_ses() when refresing dfs cache Paulo Alcantara
2 siblings, 2 replies; 7+ messages in thread
From: Paulo Alcantara @ 2021-05-11 16:36 UTC (permalink / raw)
To: linux-cifs, smfrench; +Cc: Paulo Alcantara
Define a maximum number of 16 (CIFS_MAX_ADDR_COUNT) ip addresses the
client can handle per hostname at mount and reconnect time.
mount.cifs(8) will be responsible for passing multiple 'ip=' options
in order to tell what ip addresses the given hostname resolved to.
cifs.ko will now connect to all resolved ip addresses and pick up the
one that got connected first. In case there is no upcall or it failed
for some reason, the client will reuse the resolved ip addresses that
came from userspace mount.cifs(8).
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
---
fs/cifs/cifs_dfs_ref.c | 48 +-
fs/cifs/cifsglob.h | 8 +-
fs/cifs/connect.c | 1042 ++++++++++++++++++++++++----------------
fs/cifs/dfs_cache.c | 6 +-
fs/cifs/dns_resolve.c | 72 +--
fs/cifs/dns_resolve.h | 5 +-
fs/cifs/fs_context.c | 22 +-
fs/cifs/fs_context.h | 4 +-
fs/cifs/misc.c | 47 +-
9 files changed, 758 insertions(+), 496 deletions(-)
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index c87c37cf2914..4ca718721bf9 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -143,8 +143,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
const char *prepath = NULL;
int md_len;
char *tkn_e;
- char *srvIP = NULL;
char sep = ',';
+ struct sockaddr_storage *addrs = NULL;
+ char ip[INET6_ADDRSTRLEN + 1];
+ int numaddrs, i;
int off, noff;
if (sb_mountdata == NULL)
@@ -173,21 +175,28 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
}
}
- rc = dns_resolve_server_name_to_ip(name, &srvIP);
- if (rc < 0) {
+ addrs = kmalloc(CIFS_MAX_ADDR_COUNT * sizeof(*addrs), GFP_KERNEL);
+ if (!addrs) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+
+ rc = numaddrs = dns_resolve_server_name_to_addrs(name, addrs, CIFS_MAX_ADDR_COUNT);
+ if (rc <= 0) {
cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
__func__, name, rc);
+ rc = rc == 0 ? -EINVAL : rc;
goto compose_mount_options_err;
}
/*
- * In most cases, we'll be building a shorter string than the original,
- * but we do have to assume that the address in the ip= option may be
- * much longer than the original. Add the max length of an address
- * string to the length of the original string to allow for worst case.
+ * In most cases, we'll be building a shorter string than the original, but we do have to
+ * assume that the address in the ip= option may be much longer than the original.
+ * Add the max length of CIFS_MAX_ADDR_COUNT * an address string to the length of the
+ * original string to allow for worst case.
*/
- md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
- mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
+ md_len = strlen(sb_mountdata) + CIFS_MAX_ADDR_COUNT * (INET6_ADDRSTRLEN + sizeof(",ip="));
+ mountdata = kzalloc(md_len + 1, GFP_KERNEL);
if (mountdata == NULL) {
rc = -ENOMEM;
goto compose_mount_options_err;
@@ -227,10 +236,23 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
mountdata[md_len] = '\0';
/* copy new IP and ref share name */
- if (mountdata[strlen(mountdata) - 1] != sep)
+ if (mountdata[strlen(mountdata) - 1] == sep)
+ mountdata[strlen(mountdata) - 1] = '\0';
+
+ for (i = 0; i < numaddrs; i++) {
+ struct sockaddr_storage *addr = &addrs[i];
+
+ if (addr->ss_family == AF_INET6) {
+ scnprintf(ip, sizeof(ip), "%pI6",
+ &((struct sockaddr_in6 *)addr)->sin6_addr);
+ } else
+ scnprintf(ip, sizeof(ip), "%pI4", &((struct sockaddr_in *)addr)->sin_addr);
strncat(mountdata, &sep, 1);
- strcat(mountdata, "ip=");
- strcat(mountdata, srvIP);
+ strcat(mountdata, "ip=");
+ strcat(mountdata, ip);
+ }
+
+ kfree(addrs);
if (devname)
*devname = name;
@@ -241,13 +263,13 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
compose_mount_options_out:
- kfree(srvIP);
return mountdata;
compose_mount_options_err:
kfree(mountdata);
mountdata = ERR_PTR(rc);
kfree(name);
+ kfree(addrs);
goto compose_mount_options_out;
}
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6c65e39f0509..ccc5917c2ff3 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -87,6 +87,9 @@
/* maximum number of PDUs in one compound */
#define MAX_COMPOUND 5
+/* maximum number of addresses we can handle from a resolved hostname */
+#define CIFS_MAX_ADDR_COUNT 16
+
/*
* Default number of credits to keep available for SMB3.
* This value is chosen somewhat arbitrarily. The Windows client
@@ -589,8 +592,11 @@ struct TCP_Server_Info {
enum statusEnum tcpStatus; /* what we think the status is */
char *hostname; /* hostname portion of UNC string */
struct socket *ssocket;
- struct sockaddr_storage dstaddr;
+ struct sockaddr_storage dst_addr_list[CIFS_MAX_ADDR_COUNT];
+ unsigned int dst_addr_count;
+ struct sockaddr_storage dstaddr; /* current destination address */
struct sockaddr_storage srcaddr; /* locally bind to this IP */
+ unsigned short port_num;
#ifdef CONFIG_NET_NS
struct net *net;
#endif
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 495c395f9def..43315a0365d6 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -74,51 +74,462 @@ extern bool disable_legacy_dialects;
/* Drop the connection to not overload the server */
#define NUM_STATUS_IO_TIMEOUT 5
-static int ip_connect(struct TCP_Server_Info *server);
-static int generic_ip_connect(struct TCP_Server_Info *server);
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
static void cifs_prune_tlinks(struct work_struct *work);
-/*
- * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
- * get their ip addresses changed at some point.
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key cifs_key[2];
+static struct lock_class_key cifs_slock_key[2];
+
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
+ &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
+ &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
+}
+#else
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+}
+#endif
+
+/* See RFC1001 section 14 on representation of Netbios names */
+static void rfc1002mangle(char *target, char *source, unsigned int length)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; i < (length); i++) {
+ /* mask a nibble at a time and encode */
+ target[j] = 'A' + (0x0F & (source[i] >> 4));
+ target[j+1] = 'A' + (0x0F & source[i]);
+ j += 2;
+ }
+
+}
+
+static void set_ipaddr_port(struct sockaddr_storage *addrs, unsigned int numaddrs,
+ unsigned short port)
+{
+ unsigned int i;
+
+ for (i = 0; i < numaddrs; i++)
+ cifs_set_port((struct sockaddr *)&addrs[i], port);
+}
+
+static void release_sockets(struct socket **socks, unsigned int numsocks)
+{
+ unsigned int i;
+
+ for (i = 0; i < numsocks; i++) {
+ if (socks[i])
+ sock_release(socks[i]);
+ }
+ kfree(socks);
+}
+
+static int cifs_bind_socket(struct TCP_Server_Info *server, struct socket *socket)
+{
+ int rc = 0;
+
+ if (server->srcaddr.ss_family != AF_UNSPEC) {
+ /* Bind to the specified local IP address */
+ rc = socket->ops->bind(socket, (struct sockaddr *)&server->srcaddr,
+ sizeof(server->srcaddr));
+ if (rc < 0) {
+ struct sockaddr_in *saddr4;
+ struct sockaddr_in6 *saddr6;
+
+ saddr4 = (struct sockaddr_in *)&server->srcaddr;
+ saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
+ if (saddr6->sin6_family == AF_INET6)
+ cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", &saddr6->sin6_addr, rc);
+ else
+ cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", &saddr4->sin_addr.s_addr, rc);
+ }
+ }
+ return rc;
+}
+
+static struct socket *cifs_create_socket(struct TCP_Server_Info *server, int stype,
+ struct sockaddr_storage *dstaddr)
+{
+ int rc = 0;
+ __be16 *sport;
+ int slen, sfamily;
+ struct socket *socket = NULL;
+
+ if (dstaddr->ss_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)dstaddr;
+
+ sport = &ipv6->sin6_port;
+ slen = sizeof(struct sockaddr_in6);
+ sfamily = AF_INET6;
+ cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr,
+ ntohs(*sport));
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)dstaddr;
+
+ sport = &ipv4->sin_port;
+ slen = sizeof(struct sockaddr_in);
+ sfamily = AF_INET;
+ cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr,
+ ntohs(*sport));
+ }
+
+ rc = __sock_create(cifs_net_ns(server), sfamily, stype, IPPROTO_TCP, &socket, 1);
+ if (rc < 0) {
+ cifs_dbg(VFS, "%s: Error %d creating socket\n", __func__, rc);
+ return ERR_PTR(rc);
+ }
+
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cifs_dbg(FYI, "Socket created\n");
+ socket->sk->sk_allocation = GFP_NOFS;
+ if (sfamily == AF_INET6)
+ cifs_reclassify_socket6(socket);
+ else
+ cifs_reclassify_socket4(socket);
+
+ rc = cifs_bind_socket(server, socket);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 5 * HZ;
+
+ /* make the bufsizes depend on wsize/rsize and max requests */
+ if (server->noautotune) {
+ if (socket->sk->sk_sndbuf < (200 * 1024))
+ socket->sk->sk_sndbuf = 200 * 1024;
+ if (socket->sk->sk_rcvbuf < (140 * 1024))
+ socket->sk->sk_rcvbuf = 140 * 1024;
+ }
+
+ if (server->tcp_nodelay)
+ tcp_sock_set_nodelay(socket->sk);
+
+ cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",
+ socket->sk->sk_sndbuf,
+ socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
+
+ return socket;
+}
+
+static struct socket **connect_all_ips(struct TCP_Server_Info *server,
+ struct sockaddr_storage *addrs, unsigned int numaddrs)
+{
+ unsigned int i;
+ struct socket **socks;
+ int rc = 0;
+
+ socks = kcalloc(numaddrs, sizeof(struct socket *), GFP_KERNEL);
+ if (!socks)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < numaddrs; i++) {
+ struct sockaddr_storage *addr = &addrs[i];
+ int slen = addr->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in);
+
+ socks[i] = cifs_create_socket(server, SOCK_STREAM, addr);
+ if (IS_ERR(socks[i])) {
+ rc = PTR_ERR(socks[i]);
+ socks[i] = NULL;
+ break;
+ }
+
+ rc = socks[i]->ops->connect(socks[i], (struct sockaddr *)addr, slen, O_NONBLOCK);
+#ifdef CONFIG_CIFS_DEBUG2
+ if (rc != 0 && rc != -EINPROGRESS)
+ cifs_dbg(FYI, "%s: socket connect() error: %d\n", __func__, rc);
+#endif
+ rc = 0;
+ }
+
+ if (rc) {
+ release_sockets(socks, numaddrs);
+ socks = ERR_PTR(rc);
+ }
+
+ return socks;
+}
+
+static struct socket *__get_first_connected_socket(struct socket **socks,
+ struct sockaddr_storage *addrs,
+ unsigned int numaddrs)
+{
+ unsigned int i, k;
+ struct socket *sock = ERR_PTR(-EHOSTUNREACH);
+ int rc;
+
+ for (i = 0; i < numaddrs; i++) {
+ struct sockaddr_storage naddr;
+
+ if (!socks[i])
+ continue;
+
+ rc = kernel_getpeername(socks[i], (struct sockaddr *)&naddr);
+ if (rc >= 0) {
+#ifdef CONFIG_CIFS_DEBUG2
+ struct sockaddr_in *ip4;
+ struct sockaddr_in6 *ip6;
+ __be16 *sport;
+
+ if (naddr.ss_family == AF_INET6) {
+ ip6 = (struct sockaddr_in6 *)&naddr;
+ sport = &ip6->sin6_port;
+ cifs_dbg(FYI, "%s: found a connected socket ([%pI6]:%d)\n", __func__,
+ &ip6->sin6_addr, ntohs(*sport));
+ } else {
+ ip4 = (struct sockaddr_in *)&naddr;
+ sport = &ip4->sin_port;
+ cifs_dbg(FYI, "%s: found a connected socket (%pI4:%d)\n", __func__,
+ &ip4->sin_addr, ntohs(*sport));
+ }
+
+#endif
+ for (k = 0; k < numaddrs; k++) {
+ struct sockaddr_storage *addr = &addrs[k];
+ if (cifs_match_ipaddr((struct sockaddr *)&naddr,
+ (struct sockaddr *)addr)) {
+ swap(addrs[0], *addr);
+ break;
+ }
+ }
+ sock = socks[i];
+ socks[i] = NULL;
+ break;
+ }
+ }
+ return sock;
+}
+
+static struct socket *get_first_connected_socket(struct socket **socks,
+ struct sockaddr_storage *addrs,
+ unsigned int numaddrs, bool canwait)
+{
+ struct socket *sock;
+ int retries = 0;
+
+ if (!canwait)
+ return __get_first_connected_socket(socks, addrs, numaddrs);
+
+ do {
+ msleep(1 << retries);
+ sock = __get_first_connected_socket(socks, addrs, numaddrs);
+ } while (IS_ERR(sock) && ++retries < 13);
+ return sock;
+}
+
+static int
+ip_rfc1001_connect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ /*
+ * some servers require RFC1001 sessinit before sending
+ * negprot - BB check reconnection in case where second
+ * sessinit is sent but no second negprot
+ */
+ struct rfc1002_session_packet *ses_init_buf;
+ struct smb_hdr *smb_buf;
+
+ ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
+ GFP_KERNEL);
+ if (ses_init_buf) {
+ ses_init_buf->trailer.session_req.called_len = 32;
+
+ if (server->server_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ server->server_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME,
+ RFC1001_NAME_LEN_WITH_NULL);
+
+ ses_init_buf->trailer.session_req.calling_len = 32;
+
+ /*
+ * calling name ends in null (byte 16) from old smb
+ * convention.
+ */
+ if (server->workstation_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ server->workstation_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ "LINUX_CIFS_CLNT",
+ RFC1001_NAME_LEN_WITH_NULL);
+
+ ses_init_buf->trailer.session_req.scope1 = 0;
+ ses_init_buf->trailer.session_req.scope2 = 0;
+ smb_buf = (struct smb_hdr *)ses_init_buf;
+
+ /* sizeof RFC1002_SESSION_REQUEST with no scope */
+ smb_buf->smb_buf_length = cpu_to_be32(0x81000044);
+ rc = smb_send(server, smb_buf, 0x44);
+ kfree(ses_init_buf);
+ /*
+ * RFC1001 layer in at least one server
+ * requires very short break before negprot
+ * presumably because not expecting negprot
+ * to follow so fast. This is a simple
+ * solution that works without
+ * complicating the code and causes no
+ * significant slowing down on mount
+ * for everyone else
+ */
+ usleep_range(1000, 2000);
+ }
+ /*
+ * else the negprot may still work without this
+ * even though malloc failed
+ */
+
+ return rc;
+}
+
+static int cifs_ip_connect(struct TCP_Server_Info *server)
+{
+ struct socket **socks, *sock;
+ unsigned short port = server->port_num;
+ int rc;
+ bool rootfs = server->noblockcnt;
+
+ if (port == 0) {
+ /* try with 445 port at first */
+ port = CIFS_PORT;
+
+ set_ipaddr_port(server->dst_addr_list, server->dst_addr_count, port);
+ socks = connect_all_ips(server, server->dst_addr_list, server->dst_addr_count);
+ if (IS_ERR(socks)) {
+ rc = PTR_ERR(socks);
+ cifs_server_dbg(VFS, "%s: connect_all_ips() failed: %d\n", __func__, rc);
+ return rc;
+ }
+
+ /* if it failed, try with 139 port */
+ sock = get_first_connected_socket(socks, server->dst_addr_list,
+ server->dst_addr_count, !rootfs);
+ release_sockets(socks, server->dst_addr_count);
+
+ if (IS_ERR(sock)) {
+ port = RFC1001_PORT;
+
+ set_ipaddr_port(server->dst_addr_list, server->dst_addr_count, port);
+ socks = connect_all_ips(server, server->dst_addr_list,
+ server->dst_addr_count);
+ if (IS_ERR(socks)) {
+ rc = PTR_ERR(socks);
+ cifs_server_dbg(VFS, "%s: connect_all_ips() failed: %d\n", __func__, rc);
+ return rc;
+ }
+ sock = get_first_connected_socket(socks, server->dst_addr_list,
+ server->dst_addr_count, !rootfs);
+ release_sockets(socks, server->dst_addr_count);
+ }
+ } else {
+ set_ipaddr_port(server->dst_addr_list, server->dst_addr_count, port);
+ socks = connect_all_ips(server, server->dst_addr_list, server->dst_addr_count);
+ if (IS_ERR(socks)) {
+ cifs_server_dbg(VFS, "%s: connect_all_ips() failed: %d\n", __func__, rc);
+ return PTR_ERR(socks);
+ }
+ sock = get_first_connected_socket(socks, server->dst_addr_list,
+ server->dst_addr_count, !rootfs);
+ release_sockets(socks, server->dst_addr_count);
+ }
+
+ if (IS_ERR(sock))
+ return PTR_ERR(sock);
+
+ rc = kernel_getpeername(sock, (struct sockaddr *)&server->dstaddr);
+ if (rc < 0) {
+ cifs_server_dbg(VFS, "%s: getpeername() failed with rc=%d\n",
+ __func__, rc);
+ sock_release(sock);
+ return rc;
+ }
+ server->port_num = port;
+ server->ssocket = sock;
+
+ return server->port_num == RFC1001_PORT ? ip_rfc1001_connect(server) : 0;
+}
+
+/**
+ * Set resolved address list in @addrs and number of addresses in @numaddrs.
+ *
+ * Useful for hostnames that may get their ip addresses changed at some point.
*
- * This should be called with server->srv_mutex held.
*/
-static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
+static void reconn_resolve_hostname(struct TCP_Server_Info *server, struct sockaddr_storage *addrs,
+ unsigned int *numaddrs)
{
int rc;
int len;
- char *unc, *ipaddr = NULL;
+ char *unc = NULL;
- if (!server->hostname)
- return -EINVAL;
-
- len = strlen(server->hostname) + 3;
-
- unc = kmalloc(len, GFP_KERNEL);
- if (!unc) {
- cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
- return -ENOMEM;
+ if (server->hostname) {
+ len = strlen(server->hostname) + 3;
+ unc = kmalloc(len, GFP_KERNEL);
+ if (!unc) {
+ cifs_server_dbg(VFS, "%s: failed to allocate UNC path\n", __func__);
+ cifs_server_dbg(FYI, "%s: reusing current list of ip addresses\n", __func__);
+ }
}
- scnprintf(unc, len, "\\\\%s", server->hostname);
- rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
- kfree(unc);
+ if (unc) {
+ scnprintf(unc, len, "\\\\%s", server->hostname);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
- __func__, server->hostname, rc);
- return rc;
- }
+ rc = dns_resolve_server_name_to_addrs(unc, addrs, *numaddrs);
+ kfree(unc);
- spin_lock(&cifs_tcp_ses_lock);
- rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr,
- strlen(ipaddr));
- spin_unlock(&cifs_tcp_ses_lock);
- kfree(ipaddr);
-
- return !rc ? -1 : 0;
+ if (rc <= 0) {
+ cifs_server_dbg(FYI, "%s: couldn't resolve server hostname to IP(s): %d\n", __func__, rc);
+ cifs_server_dbg(FYI, "%s: reusing current list of ip addresses\n", __func__);
+ spin_lock(&cifs_tcp_ses_lock);
+ memcpy(addrs, server->dst_addr_list,
+ sizeof(*addrs) * server->dst_addr_count);
+ *numaddrs = server->dst_addr_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ } else
+ *numaddrs = rc;
+ } else {
+ spin_lock(&cifs_tcp_ses_lock);
+ memcpy(addrs, server->dst_addr_list, sizeof(*addrs) * server->dst_addr_count);
+ *numaddrs = server->dst_addr_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+ set_ipaddr_port(addrs, *numaddrs, server->port_num);
}
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -129,7 +540,6 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
struct dfs_cache_tgt_iterator **tgt_it)
{
const char *name;
- int rc;
if (!cifs_sb || !cifs_sb->origin_fullpath)
return;
@@ -150,16 +560,9 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
server->hostname = extract_hostname(name);
if (IS_ERR(server->hostname)) {
- cifs_dbg(FYI,
- "%s: failed to extract hostname from target: %ld\n",
- __func__, PTR_ERR(server->hostname));
- return;
- }
-
- rc = reconn_set_ipaddr_from_hostname(server);
- if (rc) {
- cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
- __func__, rc);
+ cifs_dbg(VFS, "%s: failed to extract hostname from target: %ld\n", __func__,
+ PTR_ERR(server->hostname));
+ server->hostname = NULL;
}
}
@@ -195,6 +598,14 @@ cifs_reconnect(struct TCP_Server_Info *server)
struct dfs_cache_tgt_list tgt_list = {0};
struct dfs_cache_tgt_iterator *tgt_it = NULL;
#endif
+ struct sockaddr_storage *addrs = NULL;
+ unsigned int numaddrs;
+
+ addrs = kmalloc(sizeof(*addrs) * CIFS_MAX_ADDR_COUNT, GFP_KERNEL);
+ if (!addrs) {
+ rc = -ENOMEM;
+ goto out;
+ }
spin_lock(&GlobalMid_Lock);
server->nr_targets = 1;
@@ -231,8 +642,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
dfs_cache_free_tgts(&tgt_list);
cifs_put_tcp_super(sb);
#endif
- wake_up(&server->response_q);
- return rc;
+ goto out;
} else
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock);
@@ -312,41 +722,70 @@ cifs_reconnect(struct TCP_Server_Info *server)
mutex_lock(&server->srv_mutex);
-
if (!cifs_swn_set_server_dstaddr(server)) {
-#ifdef CONFIG_CIFS_DFS_UPCALL
- if (cifs_sb && cifs_sb->origin_fullpath)
- /*
- * Set up next DFS target server (if any) for reconnect. If DFS
- * feature is disabled, then we will retry last server we
- * connected to before.
- */
- reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
- else {
-#endif
+ if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
+ cifs_sb->origin_fullpath) {
+ /*
+ * Set up next DFS target server (if any) for reconnect. If DFS
+ * feature is disabled, then we will retry last server we
+ * connected to before.
+ */
+ reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
+ }
/*
* Resolve the hostname again to make sure that IP address is up-to-date.
*/
- rc = reconn_set_ipaddr_from_hostname(server);
- if (rc) {
- cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
- __func__, rc);
+ numaddrs = CIFS_MAX_ADDR_COUNT;
+ reconn_resolve_hostname(server, addrs, &numaddrs);
+
+ if (cifs_rdma_enabled(server)) {
+ /* FIXME: handle multiple ips for RDMA */
+ server->dst_addr_list[0] = server->dstaddr = addrs[0];
+ server->dst_addr_count = 1;
}
-
-#ifdef CONFIG_CIFS_DFS_UPCALL
- }
-#endif
-
-
+ } else {
+ addrs[0] = server->dstaddr;
+ numaddrs = 1;
}
- if (cifs_rdma_enabled(server))
+ if (cifs_rdma_enabled(server)) {
rc = smbd_reconnect(server);
- else
- rc = generic_ip_connect(server);
+ } else {
+ struct socket **socks, *sock;
+
+ socks = connect_all_ips(server, addrs, numaddrs);
+ if (IS_ERR(socks)) {
+ rc = PTR_ERR(socks);
+ cifs_server_dbg(VFS, "%s: connect_all_ips() failed: %d\n", __func__, rc);
+ } else {
+ mutex_unlock(&server->srv_mutex);
+ sock = get_first_connected_socket(socks, addrs, numaddrs, true);
+ release_sockets(socks, numaddrs);
+ mutex_lock(&server->srv_mutex);
+
+ if (IS_ERR(sock)) {
+ rc = PTR_ERR(sock);
+ cifs_server_dbg(FYI, "%s: couldn't find a connected socket: %d\n", __func__, rc);
+ } else {
+ rc = kernel_getpeername(sock, (struct sockaddr *)&server->dstaddr);
+ if (rc < 0) {
+ cifs_server_dbg(VFS, "%s: getpeername() failed: %d\n", __func__, rc);
+ sock_release(sock);
+ } else
+ rc = 0;
+ }
+ if (!rc) {
+ memcpy(server->dst_addr_list, addrs,
+ sizeof(addrs[0]) * numaddrs);
+ server->dst_addr_count = numaddrs;
+ server->ssocket = sock;
+ }
+ }
+ }
+
if (rc) {
- cifs_dbg(FYI, "reconnect error %d\n", rc);
mutex_unlock(&server->srv_mutex);
+ cifs_server_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
msleep(3000);
} else {
atomic_inc(&tcpSesReconnectCount);
@@ -382,7 +821,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
if (server->tcpStatus == CifsNeedNegotiate)
mod_delayed_work(cifsiod_wq, &server->echo, 0);
+out:
wake_up(&server->response_q);
+ kfree(addrs);
return rc;
}
@@ -1093,80 +1534,77 @@ cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
}
}
-/*
- * If no port is specified in addr structure, we try to match with 445 port
- * and if it fails - with 139 ports. It should be called only if address
- * families of server and addr are equal.
- */
-static bool
-match_port(struct TCP_Server_Info *server, struct sockaddr *addr)
+static bool __match_address(struct TCP_Server_Info *server, struct sockaddr_storage *caddr)
{
- __be16 port, *sport;
+ unsigned int i;
+ bool match = false;
- /* SMBDirect manages its own ports, don't match it here */
- if (server->rdma)
- return true;
+ for (i = 0; i < server->dst_addr_count && !match; i++) {
+ struct sockaddr *saddr = (struct sockaddr *)&server->dst_addr_list[i];
+ __be16 port, *sport;
- switch (addr->sa_family) {
- case AF_INET:
- sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port;
- port = ((struct sockaddr_in *) addr)->sin_port;
- break;
- case AF_INET6:
- sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port;
- port = ((struct sockaddr_in6 *) addr)->sin6_port;
- break;
- default:
- WARN_ON(1);
- return false;
- }
+ switch (saddr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)caddr;
+ struct sockaddr_in *srv_addr4 = (struct sockaddr_in *)saddr;
- if (!port) {
- port = htons(CIFS_PORT);
- if (port == *sport)
+ if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr)
+ continue;
+
+ sport = &srv_addr4->sin_port;
+ port = addr4->sin_port;
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)caddr;
+ struct sockaddr_in6 *srv_addr6 = (struct sockaddr_in6 *)saddr;
+
+ if (!ipv6_addr_equal(&addr6->sin6_addr, &srv_addr6->sin6_addr) ||
+ addr6->sin6_scope_id != srv_addr6->sin6_scope_id)
+ continue;
+
+ sport = &srv_addr6->sin6_port;
+ port = addr6->sin6_port;
+ break;
+ }
+ default:
+ /* don't expect to be here */
+ WARN_ON(1);
+ return false;
+ }
+ /* SMBDirect manages its own ports, don't match it here */
+ if (cifs_rdma_enabled(server))
return true;
-
- port = htons(RFC1001_PORT);
+ /*
+ * If no port is specified in addr structure, we try to match with 445 port and if
+ * it fails - with 139 ports. It should be called only if address families of server
+ * and addr are equal.
+ */
+ if (!port) {
+ port = htons(CIFS_PORT);
+ match |= port == *sport;
+ port = htons(RFC1001_PORT);
+ }
+ match |= port == *sport;
}
-
- return port == *sport;
+ return match;
}
-static bool
-match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
- struct sockaddr *srcaddr)
+static bool match_address(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- switch (addr->sa_family) {
- case AF_INET: {
- struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
- struct sockaddr_in *srv_addr4 =
- (struct sockaddr_in *)&server->dstaddr;
+ unsigned int i;
- if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr)
- return false;
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
- struct sockaddr_in6 *srv_addr6 =
- (struct sockaddr_in6 *)&server->dstaddr;
-
- if (!ipv6_addr_equal(&addr6->sin6_addr,
- &srv_addr6->sin6_addr))
- return false;
- if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id)
- return false;
- break;
- }
- default:
- WARN_ON(1);
- return false; /* don't expect to be here */
- }
-
- if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr))
+ if (ctx->dst_addr_count != server->dst_addr_count ||
+ (!cifs_rdma_enabled(server) && ctx->port && ctx->port != server->port_num))
return false;
- return true;
+ /* check if source and destination addresses match */
+ for (i = 0; i < ctx->dst_addr_count; i++) {
+ if (!__match_address(server, &ctx->dst_addr_list[i]))
+ return false;
+ }
+ return cifs_match_ipaddr((struct sockaddr *)&ctx->srcaddr,
+ (struct sockaddr *)&server->srcaddr);
}
static bool
@@ -1194,8 +1632,6 @@ match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
-
if (ctx->nosharesock)
return 0;
@@ -1213,11 +1649,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
return 0;
- if (!match_address(server, addr,
- (struct sockaddr *)&ctx->srcaddr))
- return 0;
-
- if (!match_port(server, addr))
+ if (!match_address(server, ctx))
return 0;
if (!match_security(server, ctx))
@@ -1364,8 +1796,17 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
mutex_init(&tcp_ses->reconnect_mutex);
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
sizeof(tcp_ses->srcaddr));
- memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
- sizeof(tcp_ses->dstaddr));
+
+ if (WARN_ON_ONCE(!ctx->dst_addr_count || ctx->dst_addr_count > CIFS_MAX_ADDR_COUNT)) {
+ rc = -EINVAL;
+ goto out_err_crypto_release;
+ }
+ memcpy(tcp_ses->dst_addr_list, ctx->dst_addr_list,
+ ctx->dst_addr_count * sizeof(ctx->dst_addr_list[0]));
+ tcp_ses->dst_addr_count = ctx->dst_addr_count;
+ memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, sizeof(tcp_ses->dstaddr));
+ tcp_ses->port_num = ctx->port;
+
if (ctx->use_client_guid)
memcpy(tcp_ses->client_guid, ctx->client_guid,
SMB2_CLIENT_GUID_SIZE);
@@ -1401,7 +1842,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
goto out_err_crypto_release;
}
}
- rc = ip_connect(tcp_ses);
+ rc = cifs_ip_connect(tcp_ses);
if (rc < 0) {
cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");
goto out_err_crypto_release;
@@ -2353,277 +2794,6 @@ cifs_match_super(struct super_block *sb, void *data)
return rc;
}
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-static struct lock_class_key cifs_key[2];
-static struct lock_class_key cifs_slock_key[2];
-
-static inline void
-cifs_reclassify_socket4(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- BUG_ON(!sock_allow_reclassification(sk));
- sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
- &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
-}
-
-static inline void
-cifs_reclassify_socket6(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- BUG_ON(!sock_allow_reclassification(sk));
- sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
- &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
-}
-#else
-static inline void
-cifs_reclassify_socket4(struct socket *sock)
-{
-}
-
-static inline void
-cifs_reclassify_socket6(struct socket *sock)
-{
-}
-#endif
-
-/* See RFC1001 section 14 on representation of Netbios names */
-static void rfc1002mangle(char *target, char *source, unsigned int length)
-{
- unsigned int i, j;
-
- for (i = 0, j = 0; i < (length); i++) {
- /* mask a nibble at a time and encode */
- target[j] = 'A' + (0x0F & (source[i] >> 4));
- target[j+1] = 'A' + (0x0F & source[i]);
- j += 2;
- }
-
-}
-
-static int
-bind_socket(struct TCP_Server_Info *server)
-{
- int rc = 0;
- if (server->srcaddr.ss_family != AF_UNSPEC) {
- /* Bind to the specified local IP address */
- struct socket *socket = server->ssocket;
- rc = socket->ops->bind(socket,
- (struct sockaddr *) &server->srcaddr,
- sizeof(server->srcaddr));
- if (rc < 0) {
- struct sockaddr_in *saddr4;
- struct sockaddr_in6 *saddr6;
- saddr4 = (struct sockaddr_in *)&server->srcaddr;
- saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
- if (saddr6->sin6_family == AF_INET6)
- cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n",
- &saddr6->sin6_addr, rc);
- else
- cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n",
- &saddr4->sin_addr.s_addr, rc);
- }
- }
- return rc;
-}
-
-static int
-ip_rfc1001_connect(struct TCP_Server_Info *server)
-{
- int rc = 0;
- /*
- * some servers require RFC1001 sessinit before sending
- * negprot - BB check reconnection in case where second
- * sessinit is sent but no second negprot
- */
- struct rfc1002_session_packet *ses_init_buf;
- struct smb_hdr *smb_buf;
- ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
- GFP_KERNEL);
- if (ses_init_buf) {
- ses_init_buf->trailer.session_req.called_len = 32;
-
- if (server->server_RFC1001_name[0] != 0)
- rfc1002mangle(ses_init_buf->trailer.
- session_req.called_name,
- server->server_RFC1001_name,
- RFC1001_NAME_LEN_WITH_NULL);
- else
- rfc1002mangle(ses_init_buf->trailer.
- session_req.called_name,
- DEFAULT_CIFS_CALLED_NAME,
- RFC1001_NAME_LEN_WITH_NULL);
-
- ses_init_buf->trailer.session_req.calling_len = 32;
-
- /*
- * calling name ends in null (byte 16) from old smb
- * convention.
- */
- if (server->workstation_RFC1001_name[0] != 0)
- rfc1002mangle(ses_init_buf->trailer.
- session_req.calling_name,
- server->workstation_RFC1001_name,
- RFC1001_NAME_LEN_WITH_NULL);
- else
- rfc1002mangle(ses_init_buf->trailer.
- session_req.calling_name,
- "LINUX_CIFS_CLNT",
- RFC1001_NAME_LEN_WITH_NULL);
-
- ses_init_buf->trailer.session_req.scope1 = 0;
- ses_init_buf->trailer.session_req.scope2 = 0;
- smb_buf = (struct smb_hdr *)ses_init_buf;
-
- /* sizeof RFC1002_SESSION_REQUEST with no scope */
- smb_buf->smb_buf_length = cpu_to_be32(0x81000044);
- rc = smb_send(server, smb_buf, 0x44);
- kfree(ses_init_buf);
- /*
- * RFC1001 layer in at least one server
- * requires very short break before negprot
- * presumably because not expecting negprot
- * to follow so fast. This is a simple
- * solution that works without
- * complicating the code and causes no
- * significant slowing down on mount
- * for everyone else
- */
- usleep_range(1000, 2000);
- }
- /*
- * else the negprot may still work without this
- * even though malloc failed
- */
-
- return rc;
-}
-
-static int
-generic_ip_connect(struct TCP_Server_Info *server)
-{
- int rc = 0;
- __be16 sport;
- int slen, sfamily;
- struct socket *socket = server->ssocket;
- struct sockaddr *saddr;
-
- saddr = (struct sockaddr *) &server->dstaddr;
-
- if (server->dstaddr.ss_family == AF_INET6) {
- struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr;
-
- sport = ipv6->sin6_port;
- slen = sizeof(struct sockaddr_in6);
- sfamily = AF_INET6;
- cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr,
- ntohs(sport));
- } else {
- struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr;
-
- sport = ipv4->sin_port;
- slen = sizeof(struct sockaddr_in);
- sfamily = AF_INET;
- cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr,
- ntohs(sport));
- }
-
- if (socket == NULL) {
- rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
- IPPROTO_TCP, &socket, 1);
- if (rc < 0) {
- cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
- server->ssocket = NULL;
- return rc;
- }
-
- /* BB other socket options to set KEEPALIVE, NODELAY? */
- cifs_dbg(FYI, "Socket created\n");
- server->ssocket = socket;
- socket->sk->sk_allocation = GFP_NOFS;
- if (sfamily == AF_INET6)
- cifs_reclassify_socket6(socket);
- else
- cifs_reclassify_socket4(socket);
- }
-
- rc = bind_socket(server);
- if (rc < 0)
- return rc;
-
- /*
- * Eventually check for other socket options to change from
- * the default. sock_setsockopt not used because it expects
- * user space buffer
- */
- socket->sk->sk_rcvtimeo = 7 * HZ;
- socket->sk->sk_sndtimeo = 5 * HZ;
-
- /* make the bufsizes depend on wsize/rsize and max requests */
- if (server->noautotune) {
- if (socket->sk->sk_sndbuf < (200 * 1024))
- socket->sk->sk_sndbuf = 200 * 1024;
- if (socket->sk->sk_rcvbuf < (140 * 1024))
- socket->sk->sk_rcvbuf = 140 * 1024;
- }
-
- if (server->tcp_nodelay)
- tcp_sock_set_nodelay(socket->sk);
-
- cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",
- socket->sk->sk_sndbuf,
- socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
-
- rc = socket->ops->connect(socket, saddr, slen,
- server->noblockcnt ? O_NONBLOCK : 0);
- /*
- * When mounting SMB root file systems, we do not want to block in
- * connect. Otherwise bail out and then let cifs_reconnect() perform
- * reconnect failover - if possible.
- */
- if (server->noblockcnt && rc == -EINPROGRESS)
- rc = 0;
- if (rc < 0) {
- cifs_dbg(FYI, "Error %d connecting to server\n", rc);
- sock_release(socket);
- server->ssocket = NULL;
- return rc;
- }
-
- if (sport == htons(RFC1001_PORT))
- rc = ip_rfc1001_connect(server);
-
- return rc;
-}
-
-static int
-ip_connect(struct TCP_Server_Info *server)
-{
- __be16 *sport;
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
-
- if (server->dstaddr.ss_family == AF_INET6)
- sport = &addr6->sin6_port;
- else
- sport = &addr->sin_port;
-
- if (*sport == 0) {
- int rc;
-
- /* try with 445 port at first */
- *sport = htons(CIFS_PORT);
-
- rc = generic_ip_connect(server);
- if (rc >= 0)
- return rc;
-
- /* if it failed, try with 139 port */
- *sport = htons(RFC1001_PORT);
- }
-
- return generic_ip_connect(server);
-}
-
void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
{
@@ -3161,20 +3331,36 @@ cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const c
}
if (mntopts) {
- char *ip;
+ char *opts, *nopts, *key, *val, sep;
+ unsigned int count = 0;
- rc = smb3_parse_opt(mntopts, "ip", &ip);
- if (rc) {
- cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc);
+ opts = kstrdup(mntopts, GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+
+ smb3_options_for_each(opts, nopts, sep, key, val) {
+ if (strcasecmp(key, "ip"))
+ continue;
+ cifs_dbg(FYI, "%s: parsed ip address: %s\n", __func__, val);
+ if (!cifs_convert_address((struct sockaddr *)&ctx->dst_addr_list[count],
+ val, strlen(val))) {
+ cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ if (++count == CIFS_MAX_ADDR_COUNT)
+ break;
+ }
+ kfree(opts);
+
+ if (!rc && !count) {
+ cifs_dbg(VFS, "%s: could not find any ip address option\n", __func__);
+ rc = -EINVAL;
+ }
+ if (rc)
return rc;
- }
- rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip));
- kfree(ip);
- if (!rc) {
- cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__);
- return -EINVAL;
- }
+ ctx->dst_addr_count = count;
}
if (ctx->nullauth) {
@@ -3185,9 +3371,11 @@ cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const c
/* BB fixme parse for domain name here */
cifs_dbg(FYI, "Username: %s\n", ctx->username);
} else {
+ /*
+ * In userspace mount helper we can get user name from alternate locations such as
+ * env variables and files on disk.
+ */
cifs_dbg(VFS, "No username specified\n");
- /* In userspace mount helper we can get user name from alternate
- locations such as env variables and files on disk */
return -EINVAL;
}
@@ -3391,6 +3579,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
char *mntdata = NULL;
bool ref_server = false;
+ cifs_dbg(FYI, "%s: mount_options=%s\n", __func__, cifs_sb->ctx->mount_options);
+
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
/*
* If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index b1fa30fefe1f..5c3129d4af1d 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1235,8 +1235,10 @@ int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
cifs_dbg(FYI, "%s: updating volume info\n", __func__);
spin_lock(&vi->ctx_lock);
- memcpy(&vi->ctx.dstaddr, &server->dstaddr,
- sizeof(vi->ctx.dstaddr));
+ memcpy(vi->ctx.dst_addr_list, server->dst_addr_list,
+ server->dst_addr_count * sizeof(server->dst_addr_list[0]));
+ vi->ctx.dst_addr_count = server->dst_addr_count;
+ memcpy(&vi->ctx.dstaddr, &server->dstaddr, sizeof(vi->ctx.dstaddr));
spin_unlock(&vi->ctx_lock);
kref_put(&vi->refcnt, vol_release);
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 534cbba72789..977ac4ba789b 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -32,25 +32,40 @@
#include "cifsproto.h"
#include "cifs_debug.h"
+static int iplist_to_addrs(char *ips, struct sockaddr_storage *addrs, int maxaddrs)
+{
+ char *ip;
+ int count = 0;
+
+ while ((ip = strsep(&ips, ",")) && count < maxaddrs) {
+ struct sockaddr_storage addr;
+
+ if (!*ip)
+ break;
+
+ cifs_dbg(FYI, "%s: add \'%s\' to the list of ip addresses\n", __func__, ip);
+ if (cifs_convert_address((struct sockaddr *)&addr, ip, strlen(ip)) > 0)
+ addrs[count++] = addr;
+ }
+ return count;
+}
+
/**
- * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
+ * dns_resolve_server_name_to_addrs - Resolve UNC server name to a list of addresses.
* @unc: UNC path specifying the server (with '/' as delimiter)
- * @ip_addr: Where to return the IP address.
+ * @addrs: Where to return the list of addresses.
+ * @maxaddrs: Maximum number of addresses.
*
- * The IP address will be returned in string form, and the caller is
- * responsible for freeing it.
- *
- * Returns length of result on success, -ve on error.
+ * Returns the number of resolved addresses, otherwise a negative error number.
*/
-int
-dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+int dns_resolve_server_name_to_addrs(const char *unc, struct sockaddr_storage *addrs, int maxaddrs)
{
struct sockaddr_storage ss;
const char *hostname, *sep;
- char *name;
+ char *ips;
int len, rc;
- if (!ip_addr || !unc)
+ if (!addrs || !maxaddrs || !unc)
return -EINVAL;
len = strlen(unc);
@@ -73,28 +88,23 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
/* Try to interpret hostname as an IPv4 or IPv6 address */
rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len);
- if (rc > 0)
- goto name_is_IP_address;
+ if (rc > 0) {
+ cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", __func__, hostname);
+ addrs[0] = ss;
+ return 1;
+ }
/* Perform the upcall */
- rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
- NULL, ip_addr, NULL, false);
- if (rc < 0)
- cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
- __func__, len, len, hostname);
- else
- cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
- __func__, len, len, hostname, *ip_addr);
+ rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, "list", &ips, NULL, false);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", __func__, len, len, hostname);
+ return rc;
+ }
+ cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n", __func__, len, len, hostname, ips);
+
+ rc = iplist_to_addrs(ips, addrs, maxaddrs);
+ cifs_dbg(FYI, "%s: num of resolved ips: %d\n", __func__, rc);
+ kfree(ips);
+
return rc;
-
-name_is_IP_address:
- name = kmalloc(len + 1, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
- memcpy(name, hostname, len);
- name[len] = 0;
- cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n",
- __func__, name);
- *ip_addr = name;
- return 0;
}
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
index d3f5d27f4d06..b6defe354278 100644
--- a/fs/cifs/dns_resolve.h
+++ b/fs/cifs/dns_resolve.h
@@ -23,8 +23,11 @@
#ifndef _DNS_RESOLVE_H
#define _DNS_RESOLVE_H
+#include <linux/net.h>
+
#ifdef __KERNEL__
-extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+extern int dns_resolve_server_name_to_addrs(const char *unc, struct sockaddr_storage *addrs,
+ int maxaddrs);
#endif /* KERNEL */
#endif /* _DNS_RESOLVE_H */
diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c
index 5d21cd905315..a5b80bed2719 100644
--- a/fs/cifs/fs_context.c
+++ b/fs/cifs/fs_context.c
@@ -591,6 +591,7 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
static int smb3_fs_context_validate(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
+ unsigned int i;
if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) {
cifs_errorf(fc, "SMB Direct requires Version >=3.0\n");
@@ -633,9 +634,12 @@ static int smb3_fs_context_validate(struct fs_context *fc)
pr_err("Unable to determine destination address\n");
return -EHOSTUNREACH;
}
+ ctx->dst_addr_list[ctx->dst_addr_count++] = ctx->dstaddr;
}
/* set the port that we got earlier */
+ for (i = 0; i < ctx->dst_addr_count; i++)
+ cifs_set_port((struct sockaddr *)&ctx->dst_addr_list[i], ctx->port);
cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port);
if (ctx->override_uid && !ctx->uid_specified) {
@@ -792,6 +796,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
int i, opt;
bool is_smb3 = !strcmp(fc->fs_type->name, "smb3");
bool skip_parsing = false;
+ struct sockaddr_storage dstaddr;
cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key);
@@ -1096,12 +1101,25 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->got_ip = false;
break;
}
- if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr,
- param->string,
+
+ if (ctx->dst_addr_count >= CIFS_MAX_ADDR_COUNT) {
+ cifs_errorf(fc, "reached max number (%u) of ip addresses. ignoring ip=%s option.\n",
+ CIFS_MAX_ADDR_COUNT, param->string);
+ break;
+ }
+
+ if (!cifs_convert_address((struct sockaddr *)&dstaddr, param->string,
strlen(param->string))) {
pr_err("bad ip= option (%s)\n", param->string);
goto cifs_parse_mount_err;
}
+
+ if (ctx->dst_addr_count == 0)
+ ctx->dstaddr = dstaddr;
+
+ cifs_dbg(FYI, "%s: add \'%s\' to the list of destination addresses\n", __func__,
+ param->string);
+ ctx->dst_addr_list[ctx->dst_addr_count++] = dstaddr;
ctx->got_ip = true;
break;
case Opt_domain:
diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h
index 2a71c8e411ac..08e863b6c9b1 100644
--- a/fs/cifs/fs_context.h
+++ b/fs/cifs/fs_context.h
@@ -247,7 +247,9 @@ struct smb3_fs_context {
struct smb_version_operations *ops;
struct smb_version_values *vals;
char *prepath;
- struct sockaddr_storage dstaddr; /* destination address */
+ struct sockaddr_storage dst_addr_list[CIFS_MAX_ADDR_COUNT]; /* list of dest addresses */
+ unsigned int dst_addr_count; /* number of dest addresses */
+ struct sockaddr_storage dstaddr; /* current destination address */
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */
unsigned int echo_interval; /* echo interval in secs */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 7b3b1ea76baf..cce3c6b709c1 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1178,43 +1178,52 @@ int match_target_ip(struct TCP_Server_Info *server,
bool *result)
{
int rc;
- char *target, *tip = NULL;
- struct sockaddr tipaddr;
+ struct sockaddr_storage *addrs = NULL;
+ unsigned int numaddrs, i, j;
+ char *target = NULL;
*result = false;
target = kzalloc(share_len + 3, GFP_KERNEL);
- if (!target) {
- rc = -ENOMEM;
- goto out;
- }
+ if (!target)
+ return -ENOMEM;
scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share);
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
- rc = dns_resolve_server_name_to_ip(target, &tip);
- if (rc < 0)
+ addrs = kcalloc(CIFS_MAX_ADDR_COUNT, sizeof(*addrs), GFP_KERNEL);
+ if (!addrs) {
+ rc = -ENOMEM;
goto out;
+ }
- cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip);
-
- if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) {
- cifs_dbg(VFS, "%s: failed to convert target ip address\n",
- __func__);
- rc = -EINVAL;
+ rc = numaddrs = dns_resolve_server_name_to_addrs(target, addrs, CIFS_MAX_ADDR_COUNT);
+ if (rc <= 0) {
+ cifs_dbg(FYI, "%s: failed to resolve server name: %d\n", __func__, rc);
+ rc = rc == 0 ? -EINVAL : rc;
goto out;
}
- *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr,
- &tipaddr);
- cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
rc = 0;
+ if (server->dst_addr_count != numaddrs)
+ goto out;
+ for (i = 0; i < numaddrs; i++) {
+ bool match = false;
+
+ for (j = 0; j < server->dst_addr_count && !match; j++)
+ match |= cifs_match_ipaddr((struct sockaddr *)&server->dst_addr_list[j],
+ (struct sockaddr *)&addrs[i]);
+ if (!match)
+ break;
+ }
+ *result = i == numaddrs;
+
out:
+ cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
kfree(target);
- kfree(tip);
-
+ kfree(addrs);
return rc;
}
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/3] cifs: fix find_root_ses() when refresing dfs cache
2021-05-11 16:36 [PATCH 0/3] Support multiple ips per hostname Paulo Alcantara
2021-05-11 16:36 ` [PATCH 1/3] cifs: introduce smb3_options_for_each() helper Paulo Alcantara
2021-05-11 16:36 ` [PATCH 2/3] cifs: handle multiple ip addresses per hostname Paulo Alcantara
@ 2021-05-11 16:36 ` Paulo Alcantara
2 siblings, 0 replies; 7+ messages in thread
From: Paulo Alcantara @ 2021-05-11 16:36 UTC (permalink / raw)
To: linux-cifs, smfrench; +Cc: Paulo Alcantara
get_tcp_server() and cifs_get_smb_ses() require most of the fs context
info in order to find the respective tcp and ses pointers, so before
calling them, dup original fs context and set new unc path with
cifs_setup_volume_info().
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
---
fs/cifs/dfs_cache.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index 5c3129d4af1d..972aba397fdb 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1430,6 +1430,12 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
if (IS_ERR(rpath))
return ERR_CAST(rpath);
+ rc = smb3_fs_context_dup(&ctx, &vi->ctx);
+ if (rc) {
+ ses = ERR_PTR(rc);
+ goto out;
+ }
+
down_read(&htable_rw_lock);
ce = lookup_cache_entry(rpath, NULL);
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/3] cifs: introduce smb3_options_for_each() helper
2021-05-11 16:36 ` [PATCH 1/3] cifs: introduce smb3_options_for_each() helper Paulo Alcantara
@ 2021-05-11 18:15 ` kernel test robot
0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2021-05-11 18:15 UTC (permalink / raw)
To: Paulo Alcantara, linux-cifs, smfrench; +Cc: kbuild-all, Paulo Alcantara
[-- Attachment #1: Type: text/plain, Size: 3250 bytes --]
Hi Paulo,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on cifs/for-next]
[also build test WARNING on v5.13-rc1 next-20210511]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
base: git://git.samba.org/sfrench/cifs-2.6.git for-next
config: microblaze-randconfig-s032-20210511 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.3-341-g8af24329-dirty
# https://github.com/0day-ci/linux/commit/9d073f7c6b721cd2d1ed181b3ec7880bf421d689
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
git checkout 9d073f7c6b721cd2d1ed181b3ec7880bf421d689
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=microblaze
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
fs/cifs/misc.c:614: warning: Function parameter or member 'cfile' not described in 'cifs_queue_oplock_break'
fs/cifs/misc.c:1017: warning: Function parameter or member 'name' not described in 'cifs_alloc_hash'
fs/cifs/misc.c:1017: warning: Function parameter or member 'shash' not described in 'cifs_alloc_hash'
fs/cifs/misc.c:1017: warning: Function parameter or member 'sdesc' not described in 'cifs_alloc_hash'
fs/cifs/misc.c:1053: warning: Function parameter or member 'shash' not described in 'cifs_free_hash'
fs/cifs/misc.c:1053: warning: Function parameter or member 'sdesc' not described in 'cifs_free_hash'
fs/cifs/misc.c:1068: warning: Function parameter or member 'rqst' not described in 'rqst_page_get_length'
fs/cifs/misc.c:1068: warning: Function parameter or member 'page' not described in 'rqst_page_get_length'
fs/cifs/misc.c:1068: warning: Function parameter or member 'len' not described in 'rqst_page_get_length'
fs/cifs/misc.c:1068: warning: Function parameter or member 'offset' not described in 'rqst_page_get_length'
fs/cifs/misc.c:1101: warning: Function parameter or member 'dst' not described in 'copy_path_name'
fs/cifs/misc.c:1101: warning: Function parameter or member 'src' not described in 'copy_path_name'
>> fs/cifs/misc.c:1289: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst
* Parse a string that is in key[=val][,key[=val]]* form.
vim +1289 fs/cifs/misc.c
1287
1288 /**
> 1289 * Parse a string that is in key[=val][,key[=val]]* form.
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 30931 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/3] cifs: handle multiple ip addresses per hostname
2021-05-11 16:36 ` [PATCH 2/3] cifs: handle multiple ip addresses per hostname Paulo Alcantara
@ 2021-05-11 18:47 ` kernel test robot
2021-05-11 18:47 ` kernel test robot
1 sibling, 0 replies; 7+ messages in thread
From: kernel test robot @ 2021-05-11 18:47 UTC (permalink / raw)
To: Paulo Alcantara, linux-cifs, smfrench; +Cc: kbuild-all, Paulo Alcantara
[-- Attachment #1: Type: text/plain, Size: 11668 bytes --]
Hi Paulo,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on cifs/for-next]
[also build test ERROR on v5.13-rc1 next-20210511]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
base: git://git.samba.org/sfrench/cifs-2.6.git for-next
config: microblaze-randconfig-s032-20210511 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.3-341-g8af24329-dirty
# https://github.com/0day-ci/linux/commit/210f8e08a6bb153136929af6da6e0a7289ba5931
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
git checkout 210f8e08a6bb153136929af6da6e0a7289ba5931
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=microblaze
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All error/warnings (new ones prefixed by >>):
fs/cifs/connect.c: In function 'cifs_create_socket':
>> fs/cifs/connect.c:177:6: warning: variable 'slen' set but not used [-Wunused-but-set-variable]
177 | int slen, sfamily;
| ^~~~
fs/cifs/connect.c: In function 'cifs_reconnect':
>> fs/cifs/connect.c:726:46: error: 'cifs_sb' undeclared (first use in this function); did you mean 'cifs_ses'?
726 | if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
| ^~~~~~~
| cifs_ses
fs/cifs/connect.c:726:46: note: each undeclared identifier is reported only once for each function it appears in
>> fs/cifs/connect.c:733:5: error: implicit declaration of function 'reconn_set_next_dfs_target' [-Werror=implicit-function-declaration]
733 | reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> fs/cifs/connect.c:733:50: error: 'tgt_list' undeclared (first use in this function)
733 | reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
| ^~~~~~~~
>> fs/cifs/connect.c:733:61: error: 'tgt_it' undeclared (first use in this function)
733 | reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
| ^~~~~~
cc1: some warnings being treated as errors
--
fs/cifs/sess.c: In function 'cifs_ses_add_channel':
>> fs/cifs/sess.c:310:1: warning: the frame size of 2608 bytes is larger than 1024 bytes [-Wframe-larger-than=]
310 | }
| ^
vim +726 fs/cifs/connect.c
577
578 /*
579 * cifs tcp session reconnection
580 *
581 * mark tcp session as reconnecting so temporarily locked
582 * mark all smb sessions as reconnecting for tcp session
583 * reconnect tcp session
584 * wake up waiters on reconnection? - (not needed currently)
585 */
586 int
587 cifs_reconnect(struct TCP_Server_Info *server)
588 {
589 int rc = 0;
590 struct list_head *tmp, *tmp2;
591 struct cifs_ses *ses;
592 struct cifs_tcon *tcon;
593 struct mid_q_entry *mid_entry;
594 struct list_head retry_list;
595 #ifdef CONFIG_CIFS_DFS_UPCALL
596 struct super_block *sb = NULL;
597 struct cifs_sb_info *cifs_sb = NULL;
598 struct dfs_cache_tgt_list tgt_list = {0};
599 struct dfs_cache_tgt_iterator *tgt_it = NULL;
600 #endif
601 struct sockaddr_storage *addrs = NULL;
602 unsigned int numaddrs;
603
604 addrs = kmalloc(sizeof(*addrs) * CIFS_MAX_ADDR_COUNT, GFP_KERNEL);
605 if (!addrs) {
606 rc = -ENOMEM;
607 goto out;
608 }
609
610 spin_lock(&GlobalMid_Lock);
611 server->nr_targets = 1;
612 #ifdef CONFIG_CIFS_DFS_UPCALL
613 spin_unlock(&GlobalMid_Lock);
614 sb = cifs_get_tcp_super(server);
615 if (IS_ERR(sb)) {
616 rc = PTR_ERR(sb);
617 cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
618 __func__, rc);
619 sb = NULL;
620 } else {
621 cifs_sb = CIFS_SB(sb);
622 rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
623 if (rc) {
624 cifs_sb = NULL;
625 if (rc != -EOPNOTSUPP) {
626 cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
627 __func__);
628 }
629 } else {
630 server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
631 }
632 }
633 cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
634 server->nr_targets);
635 spin_lock(&GlobalMid_Lock);
636 #endif
637 if (server->tcpStatus == CifsExiting) {
638 /* the demux thread will exit normally
639 next time through the loop */
640 spin_unlock(&GlobalMid_Lock);
641 #ifdef CONFIG_CIFS_DFS_UPCALL
642 dfs_cache_free_tgts(&tgt_list);
643 cifs_put_tcp_super(sb);
644 #endif
645 goto out;
646 } else
647 server->tcpStatus = CifsNeedReconnect;
648 spin_unlock(&GlobalMid_Lock);
649 server->maxBuf = 0;
650 server->max_read = 0;
651
652 cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
653 trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname);
654
655 /* before reconnecting the tcp session, mark the smb session (uid)
656 and the tid bad so they are not used until reconnected */
657 cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n",
658 __func__);
659 spin_lock(&cifs_tcp_ses_lock);
660 list_for_each(tmp, &server->smb_ses_list) {
661 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
662 ses->need_reconnect = true;
663 list_for_each(tmp2, &ses->tcon_list) {
664 tcon = list_entry(tmp2, struct cifs_tcon, tcon_list);
665 tcon->need_reconnect = true;
666 }
667 if (ses->tcon_ipc)
668 ses->tcon_ipc->need_reconnect = true;
669 }
670 spin_unlock(&cifs_tcp_ses_lock);
671
672 /* do not want to be sending data on a socket we are freeing */
673 cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
674 mutex_lock(&server->srv_mutex);
675 if (server->ssocket) {
676 cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
677 server->ssocket->state, server->ssocket->flags);
678 kernel_sock_shutdown(server->ssocket, SHUT_WR);
679 cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
680 server->ssocket->state, server->ssocket->flags);
681 sock_release(server->ssocket);
682 server->ssocket = NULL;
683 }
684 server->sequence_number = 0;
685 server->session_estab = false;
686 kfree(server->session_key.response);
687 server->session_key.response = NULL;
688 server->session_key.len = 0;
689 server->lstrp = jiffies;
690
691 /* mark submitted MIDs for retry and issue callback */
692 INIT_LIST_HEAD(&retry_list);
693 cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
694 spin_lock(&GlobalMid_Lock);
695 list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
696 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
697 kref_get(&mid_entry->refcount);
698 if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
699 mid_entry->mid_state = MID_RETRY_NEEDED;
700 list_move(&mid_entry->qhead, &retry_list);
701 mid_entry->mid_flags |= MID_DELETED;
702 }
703 spin_unlock(&GlobalMid_Lock);
704 mutex_unlock(&server->srv_mutex);
705
706 cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
707 list_for_each_safe(tmp, tmp2, &retry_list) {
708 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
709 list_del_init(&mid_entry->qhead);
710 mid_entry->callback(mid_entry);
711 cifs_mid_q_entry_release(mid_entry);
712 }
713
714 if (cifs_rdma_enabled(server)) {
715 mutex_lock(&server->srv_mutex);
716 smbd_destroy(server);
717 mutex_unlock(&server->srv_mutex);
718 }
719
720 do {
721 try_to_freeze();
722
723 mutex_lock(&server->srv_mutex);
724
725 if (!cifs_swn_set_server_dstaddr(server)) {
> 726 if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
727 cifs_sb->origin_fullpath) {
728 /*
729 * Set up next DFS target server (if any) for reconnect. If DFS
730 * feature is disabled, then we will retry last server we
731 * connected to before.
732 */
> 733 reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
734 }
735 /*
736 * Resolve the hostname again to make sure that IP address is up-to-date.
737 */
738 numaddrs = CIFS_MAX_ADDR_COUNT;
739 reconn_resolve_hostname(server, addrs, &numaddrs);
740
741 if (cifs_rdma_enabled(server)) {
742 /* FIXME: handle multiple ips for RDMA */
743 server->dst_addr_list[0] = server->dstaddr = addrs[0];
744 server->dst_addr_count = 1;
745 }
746 } else {
747 addrs[0] = server->dstaddr;
748 numaddrs = 1;
749 }
750
751 if (cifs_rdma_enabled(server)) {
752 rc = smbd_reconnect(server);
753 } else {
754 struct socket **socks, *sock;
755
756 socks = connect_all_ips(server, addrs, numaddrs);
757 if (IS_ERR(socks)) {
758 rc = PTR_ERR(socks);
759 cifs_server_dbg(VFS, "%s: connect_all_ips() failed: %d\n", __func__, rc);
760 } else {
761 mutex_unlock(&server->srv_mutex);
762 sock = get_first_connected_socket(socks, addrs, numaddrs, true);
763 release_sockets(socks, numaddrs);
764 mutex_lock(&server->srv_mutex);
765
766 if (IS_ERR(sock)) {
767 rc = PTR_ERR(sock);
768 cifs_server_dbg(FYI, "%s: couldn't find a connected socket: %d\n", __func__, rc);
769 } else {
770 rc = kernel_getpeername(sock, (struct sockaddr *)&server->dstaddr);
771 if (rc < 0) {
772 cifs_server_dbg(VFS, "%s: getpeername() failed: %d\n", __func__, rc);
773 sock_release(sock);
774 } else
775 rc = 0;
776 }
777 if (!rc) {
778 memcpy(server->dst_addr_list, addrs,
779 sizeof(addrs[0]) * numaddrs);
780 server->dst_addr_count = numaddrs;
781 server->ssocket = sock;
782 }
783 }
784 }
785
786 if (rc) {
787 mutex_unlock(&server->srv_mutex);
788 cifs_server_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
789 msleep(3000);
790 } else {
791 atomic_inc(&tcpSesReconnectCount);
792 set_credits(server, 1);
793 spin_lock(&GlobalMid_Lock);
794 if (server->tcpStatus != CifsExiting)
795 server->tcpStatus = CifsNeedNegotiate;
796 spin_unlock(&GlobalMid_Lock);
797 cifs_swn_reset_server_dstaddr(server);
798 mutex_unlock(&server->srv_mutex);
799 }
800 } while (server->tcpStatus == CifsNeedReconnect);
801
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 30931 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/3] cifs: handle multiple ip addresses per hostname
2021-05-11 16:36 ` [PATCH 2/3] cifs: handle multiple ip addresses per hostname Paulo Alcantara
2021-05-11 18:47 ` kernel test robot
@ 2021-05-11 18:47 ` kernel test robot
1 sibling, 0 replies; 7+ messages in thread
From: kernel test robot @ 2021-05-11 18:47 UTC (permalink / raw)
To: Paulo Alcantara, linux-cifs, smfrench; +Cc: kbuild-all, Paulo Alcantara
[-- Attachment #1: Type: text/plain, Size: 5930 bytes --]
Hi Paulo,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on cifs/for-next]
[also build test WARNING on v5.13-rc1 next-20210511]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
base: git://git.samba.org/sfrench/cifs-2.6.git for-next
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/210f8e08a6bb153136929af6da6e0a7289ba5931
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Paulo-Alcantara/Support-multiple-ips-per-hostname/20210512-003751
git checkout 210f8e08a6bb153136929af6da6e0a7289ba5931
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=arc
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
fs/cifs/connect.c: In function 'cifs_create_socket':
fs/cifs/connect.c:177:6: warning: variable 'slen' set but not used [-Wunused-but-set-variable]
177 | int slen, sfamily;
| ^~~~
fs/cifs/connect.c: In function 'next_dfs_prepath':
>> fs/cifs/connect.c:3540:1: warning: the frame size of 2516 bytes is larger than 1024 bytes [-Wframe-larger-than=]
3540 | }
| ^
fs/cifs/connect.c: In function 'do_dfs_failover':
fs/cifs/connect.c:3313:1: warning: the frame size of 2584 bytes is larger than 1024 bytes [-Wframe-larger-than=]
3313 | }
| ^
--
fs/cifs/dfs_cache.c: In function 'find_root_ses.isra.0':
>> fs/cifs/dfs_cache.c:1483:1: warning: the frame size of 2548 bytes is larger than 1024 bytes [-Wframe-larger-than=]
1483 | }
| ^
vim +3540 fs/cifs/connect.c
7efd081582619e Paulo Alcantara 2020-07-21 3497
ff2c54a04097de Paulo Alcantara 2021-02-24 3498 /* Set up next dfs prefix path in @dfs_path */
ff2c54a04097de Paulo Alcantara 2021-02-24 3499 static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
7efd081582619e Paulo Alcantara 2020-07-21 3500 const unsigned int xid, struct TCP_Server_Info *server,
7efd081582619e Paulo Alcantara 2020-07-21 3501 struct cifs_tcon *tcon, char **dfs_path)
56c762eb9bee33 Paulo Alcantara 2018-11-14 3502 {
ff2c54a04097de Paulo Alcantara 2021-02-24 3503 char *path, *npath;
ff2c54a04097de Paulo Alcantara 2021-02-24 3504 int added_treename = is_tcon_dfs(tcon);
ff2c54a04097de Paulo Alcantara 2021-02-24 3505 int rc;
7efd081582619e Paulo Alcantara 2020-07-21 3506
3fa1c6d1b8f5c3 Ronnie Sahlberg 2020-12-09 3507 path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
7efd081582619e Paulo Alcantara 2020-07-21 3508 if (!path)
7efd081582619e Paulo Alcantara 2020-07-21 3509 return -ENOMEM;
ce558b0e17f8a6 Steve French 2018-05-31 3510
ff2c54a04097de Paulo Alcantara 2021-02-24 3511 rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
ff2c54a04097de Paulo Alcantara 2021-02-24 3512 if (rc == -EREMOTE) {
3fa1c6d1b8f5c3 Ronnie Sahlberg 2020-12-09 3513 struct smb3_fs_context v = {NULL};
7efd081582619e Paulo Alcantara 2020-07-21 3514 /* if @path contains a tree name, skip it in the prefix path */
7efd081582619e Paulo Alcantara 2020-07-21 3515 if (added_treename) {
66e7b09c731175 Ronnie Sahlberg 2020-11-05 3516 rc = smb3_parse_devname(path, &v);
7efd081582619e Paulo Alcantara 2020-07-21 3517 if (rc)
ff2c54a04097de Paulo Alcantara 2021-02-24 3518 goto out;
7efd081582619e Paulo Alcantara 2020-07-21 3519 npath = build_unc_path_to_root(&v, cifs_sb, true);
c741cba2cd1d14 Ronnie Sahlberg 2020-12-14 3520 smb3_cleanup_fs_context_contents(&v);
7efd081582619e Paulo Alcantara 2020-07-21 3521 } else {
3fa1c6d1b8f5c3 Ronnie Sahlberg 2020-12-09 3522 v.UNC = ctx->UNC;
7efd081582619e Paulo Alcantara 2020-07-21 3523 v.prepath = path + 1;
7efd081582619e Paulo Alcantara 2020-07-21 3524 npath = build_unc_path_to_root(&v, cifs_sb, true);
5072010ccf0592 Paulo Alcantara (SUSE 2019-03-19 3525) }
ff2c54a04097de Paulo Alcantara 2021-02-24 3526
7efd081582619e Paulo Alcantara 2020-07-21 3527 if (IS_ERR(npath)) {
7efd081582619e Paulo Alcantara 2020-07-21 3528 rc = PTR_ERR(npath);
ff2c54a04097de Paulo Alcantara 2021-02-24 3529 goto out;
a6b5058fafdf50 Aurelien Aptel 2016-05-25 3530 }
ff2c54a04097de Paulo Alcantara 2021-02-24 3531
7efd081582619e Paulo Alcantara 2020-07-21 3532 kfree(*dfs_path);
7efd081582619e Paulo Alcantara 2020-07-21 3533 *dfs_path = npath;
ff2c54a04097de Paulo Alcantara 2021-02-24 3534 rc = -EREMOTE;
1bfe73c258addc Igor Mammedov 2009-04-01 3535 }
1bfe73c258addc Igor Mammedov 2009-04-01 3536
ff2c54a04097de Paulo Alcantara 2021-02-24 3537 out:
7efd081582619e Paulo Alcantara 2020-07-21 3538 kfree(path);
7efd081582619e Paulo Alcantara 2020-07-21 3539 return rc;
4a367dc0443566 Paulo Alcantara 2018-11-14 @3540 }
4a367dc0443566 Paulo Alcantara 2018-11-14 3541
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 68132 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2021-05-11 18:48 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-11 16:36 [PATCH 0/3] Support multiple ips per hostname Paulo Alcantara
2021-05-11 16:36 ` [PATCH 1/3] cifs: introduce smb3_options_for_each() helper Paulo Alcantara
2021-05-11 18:15 ` kernel test robot
2021-05-11 16:36 ` [PATCH 2/3] cifs: handle multiple ip addresses per hostname Paulo Alcantara
2021-05-11 18:47 ` kernel test robot
2021-05-11 18:47 ` kernel test robot
2021-05-11 16:36 ` [PATCH 3/3] cifs: fix find_root_ses() when refresing dfs cache Paulo Alcantara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).