All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/11] Witness protocol support for transparent failover
@ 2020-11-30 18:02 Samuel Cabrero
  2020-11-30 18:02 ` [PATCH v4 01/11] cifs: Make extract_hostname function public Samuel Cabrero
                   ` (11 more replies)
  0 siblings, 12 replies; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs

Changes from v3:
  * Add registration ID attribute to the unregister netlink message.
    The userspace daemon will include it in the log output as it is
    useful for debugging purposes.

Changes from v2:
  * Fix 'no previous prototype' kernel test robot warning in
    fs/cifs/netlink.c

Changes from v1:
  * Update SPDX header in user space API files to LGPL-2.1+ with
    Linux-syscall-note

[PATCH v4 01/11] cifs: Make extract_hostname function public
[PATCH v4 02/11] cifs: Make extract_sharename function public
[PATCH v4 03/11] cifs: Register generic netlink family
[PATCH v4 04/11] cifs: add witness mount option and data structs
[PATCH v4 05/11] cifs: Send witness register and unregister commands
[PATCH v4 06/11] cifs: Set witness notification handler for messages
[PATCH v4 07/11] cifs: Add witness information to debug data dump
[PATCH v4 08/11] cifs: Send witness register messages to userspace
[PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is
[PATCH v4 10/11] cifs: Handle witness client move notification
[PATCH v4 11/11] cifs: Handle witness share moved notification

 fs/cifs/Kconfig                        |  11 +
 fs/cifs/Makefile                       |   2 +
 fs/cifs/cache.c                        |  24 -
 fs/cifs/cifs_debug.c                   |  13 +
 fs/cifs/cifs_swn.c                     | 727 +++++++++++++++++++++++++
 fs/cifs/cifs_swn.h                     |  25 +
 fs/cifs/cifsfs.c                       |  22 +-
 fs/cifs/cifsglob.h                     |   8 +
 fs/cifs/cifsproto.h                    |   2 +
 fs/cifs/connect.c                      | 141 +++--
 fs/cifs/fscache.c                      |   1 +
 fs/cifs/fscache.h                      |   1 -
 fs/cifs/misc.c                         |  56 ++
 fs/cifs/netlink.c                      |  89 +++
 fs/cifs/netlink.h                      |  16 +
 include/uapi/linux/cifs/cifs_netlink.h |  63 +++
 16 files changed, 1122 insertions(+), 79 deletions(-)


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v4 01/11] cifs: Make extract_hostname function public
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  3:42   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 02/11] cifs: Make extract_sharename " Samuel Cabrero
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Move the function to misc.c and give it a public header.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifsproto.h |  1 +
 fs/cifs/connect.c   | 34 ----------------------------------
 fs/cifs/misc.c      | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 24c6f36177ba..d716e81d86fa 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -620,6 +620,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
 struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
 int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
+char *extract_hostname(const char *unc);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 28c1459fb0fc..a938371af6ef 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -284,7 +284,6 @@ 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);
-static char *extract_hostname(const char *unc);
 
 /*
  * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
@@ -1232,39 +1231,6 @@ cifs_demultiplex_thread(void *p)
 	module_put_and_exit(0);
 }
 
-/* extract the host portion of the UNC string */
-static char *
-extract_hostname(const char *unc)
-{
-	const char *src;
-	char *dst, *delim;
-	unsigned int len;
-
-	/* skip double chars at beginning of string */
-	/* BB: check validity of these bytes? */
-	if (strlen(unc) < 3)
-		return ERR_PTR(-EINVAL);
-	for (src = unc; *src && *src == '\\'; src++)
-		;
-	if (!*src)
-		return ERR_PTR(-EINVAL);
-
-	/* delimiter between hostname and sharename is always '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-
-	len = delim - src;
-	dst = kmalloc((len + 1), GFP_KERNEL);
-	if (dst == NULL)
-		return ERR_PTR(-ENOMEM);
-
-	memcpy(dst, src, len);
-	dst[len] = '\0';
-
-	return dst;
-}
-
 static int get_option_ul(substring_t args[], unsigned long *option)
 {
 	int rc;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 1c14cf01dbef..3d5cc25c167f 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1195,3 +1195,35 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
 	cifs_put_tcon_super(sb);
 	return rc;
 }
+
+/* extract the host portion of the UNC string */
+char *extract_hostname(const char *unc)
+{
+	const char *src;
+	char *dst, *delim;
+	unsigned int len;
+
+	/* skip double chars at beginning of string */
+	/* BB: check validity of these bytes? */
+	if (strlen(unc) < 3)
+		return ERR_PTR(-EINVAL);
+	for (src = unc; *src && *src == '\\'; src++)
+		;
+	if (!*src)
+		return ERR_PTR(-EINVAL);
+
+	/* delimiter between hostname and sharename is always '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+
+	len = delim - src;
+	dst = kmalloc((len + 1), GFP_KERNEL);
+	if (dst == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(dst, src, len);
+	dst[len] = '\0';
+
+	return dst;
+}
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 02/11] cifs: Make extract_sharename function public
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
  2020-11-30 18:02 ` [PATCH v4 01/11] cifs: Make extract_hostname function public Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:50   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 03/11] cifs: Register generic netlink family Samuel Cabrero
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Move the function to misc.c

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cache.c     | 24 ------------------------
 fs/cifs/cifsproto.h |  1 +
 fs/cifs/fscache.c   |  1 +
 fs/cifs/fscache.h   |  1 -
 fs/cifs/misc.c      | 24 ++++++++++++++++++++++++
 5 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
index 0f2adecb94f2..488fe0ffc1ef 100644
--- a/fs/cifs/cache.c
+++ b/fs/cifs/cache.c
@@ -53,30 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = {
 	.type = FSCACHE_COOKIE_TYPE_INDEX,
 };
 
-char *extract_sharename(const char *treename)
-{
-	const char *src;
-	char *delim, *dst;
-	int len;
-
-	/* skip double chars at the beginning */
-	src = treename + 2;
-
-	/* share name is always preceded by '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-	delim++;
-	len = strlen(delim);
-
-	/* caller has to free the memory */
-	dst = kstrndup(delim, len, GFP_KERNEL);
-	if (!dst)
-		return ERR_PTR(-ENOMEM);
-
-	return dst;
-}
-
 static enum
 fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
 					      const void *data,
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index d716e81d86fa..5f997a01fb45 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -621,6 +621,7 @@ struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
 int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
 char *extract_hostname(const char *unc);
+char *extract_sharename(const char *unc);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index da688185403c..20d24af33ee2 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -22,6 +22,7 @@
 #include "cifsglob.h"
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
+#include "cifsproto.h"
 
 /*
  * Key layout of CIFS server cache index object
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
index 1091633d2adb..e811f2dd7619 100644
--- a/fs/cifs/fscache.h
+++ b/fs/cifs/fscache.h
@@ -57,7 +57,6 @@ extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
 
 extern int cifs_fscache_register(void);
 extern void cifs_fscache_unregister(void);
-extern char *extract_sharename(const char *);
 
 /*
  * fscache.c
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 3d5cc25c167f..f0a1c24751b2 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1227,3 +1227,27 @@ char *extract_hostname(const char *unc)
 
 	return dst;
 }
+
+char *extract_sharename(const char *unc)
+{
+	const char *src;
+	char *delim, *dst;
+	int len;
+
+	/* skip double chars at the beginning */
+	src = unc + 2;
+
+	/* share name is always preceded by '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+	delim++;
+	len = strlen(delim);
+
+	/* caller has to free the memory */
+	dst = kstrndup(delim, len, GFP_KERNEL);
+	if (!dst)
+		return ERR_PTR(-ENOMEM);
+
+	return dst;
+}
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 03/11] cifs: Register generic netlink family
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
  2020-11-30 18:02 ` [PATCH v4 01/11] cifs: Make extract_hostname function public Samuel Cabrero
  2020-11-30 18:02 ` [PATCH v4 02/11] cifs: Make extract_sharename " Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:51   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 04/11] cifs: add witness mount option and data structs Samuel Cabrero
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Register a new generic netlink family to talk to the witness service
userspace daemon.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/Kconfig                        | 11 ++++
 fs/cifs/Makefile                       |  2 +
 fs/cifs/cifsfs.c                       | 17 ++++++-
 fs/cifs/netlink.c                      | 69 ++++++++++++++++++++++++++
 fs/cifs/netlink.h                      | 16 ++++++
 include/uapi/linux/cifs/cifs_netlink.h | 31 ++++++++++++
 6 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 fs/cifs/netlink.c
 create mode 100644 fs/cifs/netlink.h
 create mode 100644 include/uapi/linux/cifs/cifs_netlink.h

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 604f65f4b6c5..664ac5c63d39 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -190,6 +190,17 @@ config CIFS_DFS_UPCALL
 	  servers if their addresses change or for implicit mounts of
 	  DFS junction points. If unsure, say Y.
 
+config CIFS_SWN_UPCALL
+	bool "SWN feature support"
+	depends on CIFS
+	help
+	  The Service Witness Protocol (SWN) is used to get notifications
+	  from a highly available server of resource state changes. This
+	  feature enables an upcall mechanism for CIFS which contacts an
+	  userspace daemon to establish the DCE/RPC connection to retrieve
+	  the cluster available interfaces and resource change notifications.
+	  If unsure, say Y.
+
 config CIFS_NFSD_EXPORT
 	bool "Allow nfsd to export CIFS file system"
 	depends on CIFS && BROKEN
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index cd17d0e50f2a..b88fd46ac597 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -18,6 +18,8 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
 
+cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
+
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
 
 cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 472cb7777e3e..8111d0109a2e 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -55,6 +55,9 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "netlink.h"
+#endif
 
 /*
  * DOS dates from 1980/1/1 through 2107/12/31
@@ -1617,10 +1620,15 @@ init_cifs(void)
 	if (rc)
 		goto out_destroy_dfs_cache;
 #endif /* CONFIG_CIFS_UPCALL */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	rc = cifs_genl_init();
+	if (rc)
+		goto out_register_key_type;
+#endif /* CONFIG_CIFS_SWN_UPCALL */
 
 	rc = init_cifs_idmap();
 	if (rc)
-		goto out_register_key_type;
+		goto out_cifs_swn_init;
 
 	rc = register_filesystem(&cifs_fs_type);
 	if (rc)
@@ -1636,7 +1644,11 @@ init_cifs(void)
 
 out_init_cifs_idmap:
 	exit_cifs_idmap();
+out_cifs_swn_init:
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
 out_register_key_type:
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 out_destroy_dfs_cache:
@@ -1673,6 +1685,9 @@ exit_cifs(void)
 	unregister_filesystem(&smb3_fs_type);
 	cifs_dfs_release_automount_timer();
 	exit_cifs_idmap();
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 #endif
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
new file mode 100644
index 000000000000..b9154661fa85
--- /dev/null
+++ b/fs/cifs/netlink.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#include <net/genetlink.h>
+#include <uapi/linux/cifs/cifs_netlink.h>
+
+#include "netlink.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+
+static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
+};
+
+static struct genl_ops cifs_genl_ops[] = {
+};
+
+static const struct genl_multicast_group cifs_genl_mcgrps[] = {
+	[CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
+};
+
+struct genl_family cifs_genl_family = {
+	.name		= CIFS_GENL_NAME,
+	.version	= CIFS_GENL_VERSION,
+	.hdrsize	= 0,
+	.maxattr	= CIFS_GENL_ATTR_MAX,
+	.module		= THIS_MODULE,
+	.policy		= cifs_genl_policy,
+	.ops		= cifs_genl_ops,
+	.n_ops		= ARRAY_SIZE(cifs_genl_ops),
+	.mcgrps		= cifs_genl_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(cifs_genl_mcgrps),
+};
+
+/**
+ * cifs_genl_init - Register generic netlink family
+ *
+ * Return zero if initialized successfully, otherwise non-zero.
+ */
+int cifs_genl_init(void)
+{
+	int ret;
+
+	ret = genl_register_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to register netlink family\n",
+				__func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * cifs_genl_exit - Unregister generic netlink family
+ */
+void cifs_genl_exit(void)
+{
+	int ret;
+
+	ret = genl_unregister_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to unregister netlink family\n",
+				__func__);
+	}
+}
diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h
new file mode 100644
index 000000000000..e2fa8ed24c54
--- /dev/null
+++ b/fs/cifs/netlink.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#ifndef _CIFS_NETLINK_H
+#define _CIFS_NETLINK_H
+
+extern struct genl_family cifs_genl_family;
+
+extern int cifs_genl_init(void);
+extern void cifs_genl_exit(void);
+
+#endif /* _CIFS_NETLINK_H */
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
new file mode 100644
index 000000000000..cdb1bd78fbc7
--- /dev/null
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+
+#ifndef _UAPILINUX_CIFS_NETLINK_H
+#define _UAPILINUX_CIFS_NETLINK_H
+
+#define CIFS_GENL_NAME			"cifs"
+#define CIFS_GENL_VERSION		0x1
+
+#define CIFS_GENL_MCGRP_SWN_NAME	"cifs_mcgrp_swn"
+
+enum cifs_genl_multicast_groups {
+	CIFS_GENL_MCGRP_SWN,
+};
+
+enum cifs_genl_attributes {
+	__CIFS_GENL_ATTR_MAX,
+};
+#define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
+
+enum cifs_genl_commands {
+	__CIFS_GENL_CMD_MAX
+};
+#define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
+
+#endif /* _UAPILINUX_CIFS_NETLINK_H */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 04/11] cifs: add witness mount option and data structs
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (2 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 03/11] cifs: Register generic netlink family Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:07   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon Samuel Cabrero
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Add 'witness' mount option to register for witness notifications.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifsfs.c   |  5 +++++
 fs/cifs/cifsglob.h |  4 ++++
 fs/cifs/connect.c  | 35 ++++++++++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8111d0109a2e..c2bbc444b463 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -637,6 +637,11 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 		seq_printf(s, ",multichannel,max_channels=%zu",
 			   tcon->ses->chan_max);
 
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	if (tcon->use_witness)
+		seq_puts(s, ",witness");
+#endif
+
 	return 0;
 }
 
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 484ec2d8c5c9..f45b7c0fbceb 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -619,6 +619,7 @@ struct smb_vol {
 	unsigned int max_channels;
 	__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
 	bool rootfs:1; /* if it's a SMB root file system */
+	bool witness:1; /* use witness protocol */
 };
 
 /**
@@ -1177,6 +1178,9 @@ struct cifs_tcon {
 	int remap:2;
 	struct list_head ulist; /* cache update list */
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	bool use_witness:1; /* use witness protocol */
+#endif
 };
 
 /*
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a938371af6ef..22d46c8acc7f 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -102,7 +102,7 @@ enum {
 	Opt_resilient, Opt_noresilient,
 	Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
 	Opt_multichannel, Opt_nomultichannel,
-	Opt_compress,
+	Opt_compress, Opt_witness,
 
 	/* Mount options which take numeric value */
 	Opt_backupuid, Opt_backupgid, Opt_uid,
@@ -276,6 +276,7 @@ static const match_table_t cifs_mount_option_tokens = {
 	{ Opt_ignore, "relatime" },
 	{ Opt_ignore, "_netdev" },
 	{ Opt_rootfs, "rootfs" },
+	{ Opt_witness, "witness" },
 
 	{ Opt_err, NULL }
 };
@@ -1540,6 +1541,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 			vol->rootfs = true;
 #endif
 			break;
+		case Opt_witness:
+#ifndef CONFIG_CIFS_SWN_UPCALL
+			cifs_dbg(VFS, "Witness support needs CONFIG_CIFS_SWN_UPCALL kernel config option set\n");
+			goto cifs_parse_mount_err;
+#endif
+			vol->witness = true;
+			break;
 		case Opt_posixpaths:
 			vol->posix_paths = 1;
 			break;
@@ -3160,6 +3168,8 @@ cifs_put_tcon(struct cifs_tcon *tcon)
 		return;
 	}
 
+	/* TODO witness unregister */
+
 	list_del_init(&tcon->tcon_list);
 	spin_unlock(&cifs_tcp_ses_lock);
 
@@ -3321,6 +3331,26 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
 		tcon->use_resilient = true;
 	}
 
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	tcon->use_witness = false;
+	if (volume_info->witness) {
+		if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
+			if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
+				/* TODO witness register */
+				tcon->use_witness = true;
+			} else {
+				cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
+				rc = -EOPNOTSUPP;
+				goto out_fail;
+			}
+		} else {
+			cifs_dbg(VFS, "SMB3 or later required for witness option\n");
+			rc = -EOPNOTSUPP;
+			goto out_fail;
+		}
+	}
+#endif
+
 	/* If the user really knows what they are doing they can override */
 	if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
 		if (volume_info->cache_ro)
@@ -5072,6 +5102,9 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
 	vol_info->sectype = master_tcon->ses->sectype;
 	vol_info->sign = master_tcon->ses->sign;
 	vol_info->seal = master_tcon->seal;
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	vol_info->witness = master_tcon->use_witness;
+#endif
 
 	rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
 	if (rc) {
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (3 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 04/11] cifs: add witness mount option and data structs Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:52   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 06/11] cifs: Set witness notification handler for messages from " Samuel Cabrero
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

+ Define the generic netlink family commands and message attributes to
  communicate with the userspace daemon

+ The register and unregister commands are sent when connecting or
  disconnecting a tree. The witness registration keeps a pointer to
  the tcon and has the same lifetime.

+ Each registration has an id allocated by an IDR. This id is sent to the
  userspace daemon in the register command, and will be included in the
  notification messages from the userspace daemon to retrieve from the
  IDR the matching registration.

+ The authentication information is bundled in the register message.
  If kerberos is used the message just carries a flag.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/Makefile                       |   2 +-
 fs/cifs/cifs_swn.c                     | 421 +++++++++++++++++++++++++
 fs/cifs/cifs_swn.h                     |  17 +
 fs/cifs/connect.c                      |  26 +-
 fs/cifs/netlink.c                      |  11 +
 include/uapi/linux/cifs/cifs_netlink.h |  15 +
 6 files changed, 489 insertions(+), 3 deletions(-)
 create mode 100644 fs/cifs/cifs_swn.c
 create mode 100644 fs/cifs/cifs_swn.h

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index b88fd46ac597..abb2fbc0f904 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -18,7 +18,7 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
 
-cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
+cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
 
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
 
diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
new file mode 100644
index 000000000000..c0af03955d0c
--- /dev/null
+++ b/fs/cifs/cifs_swn.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Witness Service client for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#include <linux/kref.h>
+#include <net/genetlink.h>
+#include <uapi/linux/cifs/cifs_netlink.h>
+
+#include "cifs_swn.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "fscache.h"
+#include "cifs_debug.h"
+#include "netlink.h"
+
+static DEFINE_IDR(cifs_swnreg_idr);
+static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
+
+struct cifs_swn_reg {
+	int id;
+	struct kref ref_count;
+
+	const char *net_name;
+	const char *share_name;
+	bool net_name_notify;
+	bool share_name_notify;
+	bool ip_notify;
+
+	struct cifs_tcon *tcon;
+};
+
+static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
+{
+	int ret;
+
+	ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
+{
+	int ret;
+
+	if (tcon->ses->user_name != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (tcon->ses->password != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (tcon->ses->domainName != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Sends a register message to the userspace daemon based on the registration.
+ * The authentication information to connect to the witness service is bundled
+ * into the message.
+ */
+static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
+{
+	struct sk_buff *skb;
+	struct genlmsghdr *hdr;
+	enum securityEnum authtype;
+	int ret;
+
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
+	if (hdr == NULL) {
+		ret = -ENOMEM;
+		goto nlmsg_fail;
+	}
+
+	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+			&swnreg->tcon->ses->server->dstaddr);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	if (swnreg->net_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->share_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->ip_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
+	switch (authtype) {
+	case Kerberos:
+		ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
+			goto nlmsg_fail;
+		}
+		break;
+	case LANMAN:
+	case NTLM:
+	case NTLMv2:
+	case RawNTLMSSP:
+		ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
+			goto nlmsg_fail;
+		}
+		break;
+	default:
+		cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
+		ret = -EINVAL;
+		goto nlmsg_fail;
+	}
+
+	genlmsg_end(skb, hdr);
+	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
+
+	cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
+			swnreg->net_name, swnreg->id);
+
+	return 0;
+
+nlmsg_fail:
+	genlmsg_cancel(skb, hdr);
+	nlmsg_free(skb);
+fail:
+	return ret;
+}
+
+/*
+ * Sends an uregister message to the userspace daemon based on the registration
+ */
+static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
+{
+	struct sk_buff *skb;
+	struct genlmsghdr *hdr;
+	int ret;
+
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
+	if (hdr == NULL) {
+		ret = -ENOMEM;
+		goto nlmsg_fail;
+	}
+
+	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+			&swnreg->tcon->ses->server->dstaddr);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	if (swnreg->net_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->share_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->ip_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	genlmsg_end(skb, hdr);
+	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
+
+	cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
+			swnreg->net_name, swnreg->id);
+
+	return 0;
+
+nlmsg_fail:
+	genlmsg_cancel(skb, hdr);
+	nlmsg_free(skb);
+	return ret;
+}
+
+/*
+ * Try to find a matching registration for the tcon's server name and share name.
+ * Calls to this funciton must be protected by cifs_swnreg_idr_mutex.
+ * TODO Try to avoid memory allocations
+ */
+static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+	int id;
+	const char *share_name;
+	const char *net_name;
+
+	net_name = extract_hostname(tcon->treeName);
+	if (IS_ERR_OR_NULL(net_name)) {
+		int ret;
+
+		ret = PTR_ERR(net_name);
+		cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
+				__func__, tcon->treeName, ret);
+		return NULL;
+	}
+
+	share_name = extract_sharename(tcon->treeName);
+	if (IS_ERR_OR_NULL(share_name)) {
+		int ret;
+
+		ret = PTR_ERR(net_name);
+		cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
+				__func__, tcon->treeName, ret);
+		kfree(net_name);
+		return NULL;
+	}
+
+	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+		if (strcasecmp(swnreg->net_name, net_name) != 0
+		    || strcasecmp(swnreg->share_name, share_name) != 0) {
+			continue;
+		}
+
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+
+		cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
+				swnreg->share_name);
+
+		kfree(net_name);
+		kfree(share_name);
+
+		return swnreg;
+	}
+
+	kfree(net_name);
+	kfree(share_name);
+
+	return NULL;
+}
+
+/*
+ * Get a registration for the tcon's server and share name, allocating a new one if it does not
+ * exists
+ */
+static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *reg = NULL;
+	int ret;
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+
+	/* Check if we are already registered for this network and share names */
+	reg = cifs_find_swn_reg(tcon);
+	if (IS_ERR(reg)) {
+		return reg;
+	} else if (reg != NULL) {
+		kref_get(&reg->ref_count);
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return reg;
+	}
+
+	reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
+	if (reg == NULL) {
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	kref_init(&reg->ref_count);
+
+	reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
+	if (reg->id < 0) {
+		cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
+		ret = reg->id;
+		goto fail;
+	}
+
+	reg->net_name = extract_hostname(tcon->treeName);
+	if (IS_ERR(reg->net_name)) {
+		ret = PTR_ERR(reg->net_name);
+		cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
+		goto fail_idr;
+	}
+
+	reg->share_name = extract_sharename(tcon->treeName);
+	if (IS_ERR(reg->share_name)) {
+		ret = PTR_ERR(reg->share_name);
+		cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
+		goto fail_net_name;
+	}
+
+	reg->net_name_notify = true;
+	reg->share_name_notify = true;
+	reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
+
+	reg->tcon = tcon;
+
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+
+	return reg;
+
+fail_net_name:
+	kfree(reg->net_name);
+fail_idr:
+	idr_remove(&cifs_swnreg_idr, reg->id);
+fail:
+	kfree(reg);
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+	return ERR_PTR(ret);
+}
+
+static void cifs_swn_reg_release(struct kref *ref)
+{
+	struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
+	int ret;
+
+	ret = cifs_swn_send_unregister_message(swnreg);
+	if (ret < 0)
+		cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
+
+	idr_remove(&cifs_swnreg_idr, swnreg->id);
+	kfree(swnreg->net_name);
+	kfree(swnreg->share_name);
+	kfree(swnreg);
+}
+
+static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
+{
+	mutex_lock(&cifs_swnreg_idr_mutex);
+	kref_put(&swnreg->ref_count, cifs_swn_reg_release);
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+}
+
+int cifs_swn_register(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+	int ret;
+
+	swnreg = cifs_get_swn_reg(tcon);
+	if (IS_ERR(swnreg))
+		return PTR_ERR(swnreg);
+
+	ret = cifs_swn_send_register_message(swnreg);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
+		/* Do not put the swnreg or return error, the echo task will retry */
+	}
+
+	return 0;
+}
+
+int cifs_swn_unregister(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+
+	swnreg = cifs_find_swn_reg(tcon);
+	if (swnreg == NULL) {
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return -EEXIST;
+	}
+
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+
+	cifs_put_swn_reg(swnreg);
+
+	return 0;
+}
diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
new file mode 100644
index 000000000000..69c7bd1035da
--- /dev/null
+++ b/fs/cifs/cifs_swn.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Witness Service client for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#ifndef _CIFS_SWN_H
+#define _CIFS_SWN_H
+
+struct cifs_tcon;
+
+extern int cifs_swn_register(struct cifs_tcon *tcon);
+
+extern int cifs_swn_unregister(struct cifs_tcon *tcon);
+
+#endif /* _CIFS_SWN_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 22d46c8acc7f..7fbb201b42c3 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -62,6 +62,9 @@
 #include "dfs_cache.h"
 #endif
 #include "fs_context.h"
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "cifs_swn.h"
+#endif
 
 extern mempool_t *cifs_req_poolp;
 extern bool disable_legacy_dialects;
@@ -3168,7 +3171,17 @@ cifs_put_tcon(struct cifs_tcon *tcon)
 		return;
 	}
 
-	/* TODO witness unregister */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	if (tcon->use_witness) {
+		int rc;
+
+		rc = cifs_swn_unregister(tcon);
+		if (rc < 0) {
+			cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
+					__func__, rc);
+		}
+	}
+#endif
 
 	list_del_init(&tcon->tcon_list);
 	spin_unlock(&cifs_tcp_ses_lock);
@@ -3336,8 +3349,17 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
 	if (volume_info->witness) {
 		if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
 			if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
-				/* TODO witness register */
+				/*
+				 * Set witness in use flag in first place
+				 * to retry registration in the echo task
+				 */
 				tcon->use_witness = true;
+				/* And try to register immediately */
+				rc = cifs_swn_register(tcon);
+				if (rc < 0) {
+					cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc);
+					goto out_fail;
+				}
 			} else {
 				cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
 				rc = -EOPNOTSUPP;
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
index b9154661fa85..83008a56def5 100644
--- a/fs/cifs/netlink.c
+++ b/fs/cifs/netlink.c
@@ -13,6 +13,17 @@
 #include "cifs_debug.h"
 
 static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
+	[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]	= { .type = NLA_U32 },
+	[CIFS_GENL_ATTR_SWN_NET_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_SHARE_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_IP]			= { .len = sizeof(struct sockaddr_storage) },
+	[CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY]	= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY]	= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_IP_NOTIFY]		= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_KRB_AUTH]		= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_USER_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_PASSWORD]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_DOMAIN_NAME]	= { .type = NLA_STRING },
 };
 
 static struct genl_ops cifs_genl_ops[] = {
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
index cdb1bd78fbc7..5662e2774513 100644
--- a/include/uapi/linux/cifs/cifs_netlink.h
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -19,11 +19,26 @@ enum cifs_genl_multicast_groups {
 };
 
 enum cifs_genl_attributes {
+	CIFS_GENL_ATTR_UNSPEC,
+	CIFS_GENL_ATTR_SWN_REGISTRATION_ID,
+	CIFS_GENL_ATTR_SWN_NET_NAME,
+	CIFS_GENL_ATTR_SWN_SHARE_NAME,
+	CIFS_GENL_ATTR_SWN_IP,
+	CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY,
+	CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY,
+	CIFS_GENL_ATTR_SWN_IP_NOTIFY,
+	CIFS_GENL_ATTR_SWN_KRB_AUTH,
+	CIFS_GENL_ATTR_SWN_USER_NAME,
+	CIFS_GENL_ATTR_SWN_PASSWORD,
+	CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
 	__CIFS_GENL_ATTR_MAX,
 };
 #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
 
 enum cifs_genl_commands {
+	CIFS_GENL_CMD_UNSPEC,
+	CIFS_GENL_CMD_SWN_REGISTER,
+	CIFS_GENL_CMD_SWN_UNREGISTER,
 	__CIFS_GENL_CMD_MAX
 };
 #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 06/11] cifs: Set witness notification handler for messages from userspace daemon
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (4 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:55   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 07/11] cifs: Add witness information to debug data dump Samuel Cabrero
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

+ Set a handler for the witness notification messages received from the
  userspace daemon.

+ Handle the resource state change notification. When the resource
  becomes unavailable or available set the tcp status to
  CifsNeedReconnect for all channels.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifs_swn.c                     | 86 ++++++++++++++++++++++++++
 fs/cifs/cifs_swn.h                     |  4 ++
 fs/cifs/netlink.c                      |  9 +++
 include/uapi/linux/cifs/cifs_netlink.h | 17 +++++
 4 files changed, 116 insertions(+)

diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
index c0af03955d0c..63b0764af5d5 100644
--- a/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -383,6 +383,92 @@ static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
 	mutex_unlock(&cifs_swnreg_idr_mutex);
 }
 
+static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
+{
+	int i;
+
+	switch (state) {
+	case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
+		cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
+		for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
+			spin_lock(&GlobalMid_Lock);
+			if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
+				swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+			spin_unlock(&GlobalMid_Lock);
+		}
+		break;
+	case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
+		cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
+		for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
+			spin_lock(&GlobalMid_Lock);
+			if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
+				swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+			spin_unlock(&GlobalMid_Lock);
+		}
+		break;
+	case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
+		cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
+		break;
+	}
+	return 0;
+}
+
+int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cifs_swn_reg *swnreg;
+	char name[256];
+	int type;
+
+	if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
+		int swnreg_id;
+
+		swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
+		mutex_lock(&cifs_swnreg_idr_mutex);
+		swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		if (swnreg == NULL) {
+			cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
+			return -EINVAL;
+		}
+	} else {
+		cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
+		return -EINVAL;
+	}
+
+	if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
+		type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
+	} else {
+		cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (type) {
+	case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: {
+		int state;
+
+		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) {
+			nla_strlcpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME],
+					sizeof(name));
+		} else {
+			cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
+			return -EINVAL;
+		}
+		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
+			state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
+		} else {
+			cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
+			return -EINVAL;
+		}
+		return cifs_swn_resource_state_changed(swnreg, name, state);
+	}
+	default:
+		cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
+		break;
+	}
+
+	return 0;
+}
+
 int cifs_swn_register(struct cifs_tcon *tcon)
 {
 	struct cifs_swn_reg *swnreg;
diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
index 69c7bd1035da..7ef9ecedbd05 100644
--- a/fs/cifs/cifs_swn.h
+++ b/fs/cifs/cifs_swn.h
@@ -9,9 +9,13 @@
 #define _CIFS_SWN_H
 
 struct cifs_tcon;
+struct sk_buff;
+struct genl_info;
 
 extern int cifs_swn_register(struct cifs_tcon *tcon);
 
 extern int cifs_swn_unregister(struct cifs_tcon *tcon);
 
+extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
+
 #endif /* _CIFS_SWN_H */
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
index 83008a56def5..5aaabe4cc0a7 100644
--- a/fs/cifs/netlink.c
+++ b/fs/cifs/netlink.c
@@ -11,6 +11,7 @@
 #include "netlink.h"
 #include "cifsglob.h"
 #include "cifs_debug.h"
+#include "cifs_swn.h"
 
 static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
 	[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]	= { .type = NLA_U32 },
@@ -24,9 +25,17 @@ static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
 	[CIFS_GENL_ATTR_SWN_USER_NAME]		= { .type = NLA_STRING },
 	[CIFS_GENL_ATTR_SWN_PASSWORD]		= { .type = NLA_STRING },
 	[CIFS_GENL_ATTR_SWN_DOMAIN_NAME]	= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]	= { .type = NLA_U32 },
+	[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]	= { .type = NLA_U32 },
+	[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]	= { .type = NLA_STRING},
 };
 
 static struct genl_ops cifs_genl_ops[] = {
+	{
+		.cmd = CIFS_GENL_CMD_SWN_NOTIFY,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = cifs_swn_notify,
+	},
 };
 
 static const struct genl_multicast_group cifs_genl_mcgrps[] = {
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
index 5662e2774513..da3107582f49 100644
--- a/include/uapi/linux/cifs/cifs_netlink.h
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -31,6 +31,9 @@ enum cifs_genl_attributes {
 	CIFS_GENL_ATTR_SWN_USER_NAME,
 	CIFS_GENL_ATTR_SWN_PASSWORD,
 	CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
+	CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE,
+	CIFS_GENL_ATTR_SWN_RESOURCE_STATE,
+	CIFS_GENL_ATTR_SWN_RESOURCE_NAME,
 	__CIFS_GENL_ATTR_MAX,
 };
 #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
@@ -39,8 +42,22 @@ enum cifs_genl_commands {
 	CIFS_GENL_CMD_UNSPEC,
 	CIFS_GENL_CMD_SWN_REGISTER,
 	CIFS_GENL_CMD_SWN_UNREGISTER,
+	CIFS_GENL_CMD_SWN_NOTIFY,
 	__CIFS_GENL_CMD_MAX
 };
 #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
 
+enum cifs_swn_notification_type {
+	CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE = 0x01,
+	CIFS_SWN_NOTIFICATION_CLIENT_MOVE	 = 0x02,
+	CIFS_SWN_NOTIFICATION_SHARE_MOVE	 = 0x03,
+	CIFS_SWN_NOTIFICATION_IP_CHANGE	 = 0x04,
+};
+
+enum cifs_swn_resource_state {
+	CIFS_SWN_RESOURCE_STATE_UNKNOWN     = 0x00,
+	CIFS_SWN_RESOURCE_STATE_AVAILABLE   = 0x01,
+	CIFS_SWN_RESOURCE_STATE_UNAVAILABLE = 0xFF
+};
+
 #endif /* _UAPILINUX_CIFS_NETLINK_H */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 07/11] cifs: Add witness information to debug data dump
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (5 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 06/11] cifs: Set witness notification handler for messages from " Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  5:57   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task Samuel Cabrero
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

+ Indicate if witness feature is supported
+ Indicate if witness is used when dumping tcons
+ Dumps witness registrations. Example:
  Witness registrations:
  Id: 1 Refs: 1 Network name: 'fs.fover.ad'(y) Share name: 'share1'(y) \
    Ip address: 192.168.103.200(n)

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifs_debug.c | 13 +++++++++++++
 fs/cifs/cifs_swn.c   | 35 +++++++++++++++++++++++++++++++++++
 fs/cifs/cifs_swn.h   |  2 ++
 3 files changed, 50 insertions(+)

diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 53588d7517b4..b231dcf1d1f9 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -23,6 +23,9 @@
 #ifdef CONFIG_CIFS_SMB_DIRECT
 #include "smbdirect.h"
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "cifs_swn.h"
+#endif
 
 void
 cifs_dump_mem(char *label, void *data, int length)
@@ -115,6 +118,10 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
 		seq_printf(m, " POSIX Extensions");
 	if (tcon->ses->server->ops->dump_share_caps)
 		tcon->ses->server->ops->dump_share_caps(m, tcon);
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	if (tcon->use_witness)
+		seq_puts(m, " Witness");
+#endif
 
 	if (tcon->need_reconnect)
 		seq_puts(m, "\tDISCONNECTED ");
@@ -262,6 +269,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 	seq_printf(m, ",XATTR");
 #endif
 	seq_printf(m, ",ACL");
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	seq_puts(m, ",WITNESS");
+#endif
 	seq_putc(m, '\n');
 	seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
 	seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
@@ -462,6 +472,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 	spin_unlock(&cifs_tcp_ses_lock);
 	seq_putc(m, '\n');
 
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_swn_dump(m);
+#endif
 	/* BB add code to dump additional info such as TCP session info now */
 	return 0;
 }
diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
index 63b0764af5d5..140a53a19aa0 100644
--- a/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -505,3 +505,38 @@ int cifs_swn_unregister(struct cifs_tcon *tcon)
 
 	return 0;
 }
+
+void cifs_swn_dump(struct seq_file *m)
+{
+	struct cifs_swn_reg *swnreg;
+	struct sockaddr_in *sa;
+	struct sockaddr_in6 *sa6;
+	int id;
+
+	seq_puts(m, "Witness registrations:");
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+		seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
+				id, kref_read(&swnreg->ref_count),
+				swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
+				swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
+		switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
+		case AF_INET:
+			sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
+			seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
+			break;
+		case AF_INET6:
+			sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
+			seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
+			if (sa6->sin6_scope_id)
+				seq_printf(m, "%%%u", sa6->sin6_scope_id);
+			break;
+		default:
+			seq_puts(m, "(unknown)");
+		}
+		seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
+	}
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+	seq_puts(m, "\n");
+}
diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
index 7ef9ecedbd05..13b25cdc9295 100644
--- a/fs/cifs/cifs_swn.h
+++ b/fs/cifs/cifs_swn.h
@@ -18,4 +18,6 @@ extern int cifs_swn_unregister(struct cifs_tcon *tcon);
 
 extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
 
+extern void cifs_swn_dump(struct seq_file *m);
+
 #endif /* _CIFS_SWN_H */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (6 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 07/11] cifs: Add witness information to debug data dump Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  6:00   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled Samuel Cabrero
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

If the daemon starts after mounting a share, or if it crashes, this
provides a mechanism to register again.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifs_swn.c | 15 +++++++++++++++
 fs/cifs/cifs_swn.h |  2 ++
 fs/cifs/connect.c  |  5 +++++
 3 files changed, 22 insertions(+)

diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
index 140a53a19aa0..642c9eedc8ab 100644
--- a/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -540,3 +540,18 @@ void cifs_swn_dump(struct seq_file *m)
 	mutex_unlock(&cifs_swnreg_idr_mutex);
 	seq_puts(m, "\n");
 }
+
+void cifs_swn_check(void)
+{
+	struct cifs_swn_reg *swnreg;
+	int id;
+	int ret;
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+		ret = cifs_swn_send_register_message(swnreg);
+		if (ret < 0)
+			cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
+	}
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+}
diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
index 13b25cdc9295..236ecd4959d5 100644
--- a/fs/cifs/cifs_swn.h
+++ b/fs/cifs/cifs_swn.h
@@ -20,4 +20,6 @@ extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
 
 extern void cifs_swn_dump(struct seq_file *m);
 
+extern void cifs_swn_check(void);
+
 #endif /* _CIFS_SWN_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 7fbb201b42c3..a298518bebb2 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -613,6 +613,11 @@ cifs_echo_request(struct work_struct *work)
 		cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
 			 server->hostname);
 
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	/* Check witness registrations */
+	cifs_swn_check();
+#endif
+
 requeue_echo:
 	queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
 }
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (7 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-12  6:07   ` Steve French
  2020-11-30 18:02 ` [PATCH v4 10/11] cifs: Handle witness client move notification Samuel Cabrero
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Some witness notifications, like client move, tell the client to
reconnect to a specific IP address. In this situation the DFS failover
code path has to be skipped so clean up as much as possible the
cifs_reconnect() code.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/connect.c | 21 ++++++++-------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a298518bebb2..3af88711643b 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -296,7 +296,7 @@ static void cifs_prune_tlinks(struct work_struct *work);
  * This should be called with server->srv_mutex held.
  */
 #ifdef CONFIG_CIFS_DFS_UPCALL
-static int reconn_set_ipaddr(struct TCP_Server_Info *server)
+static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
 {
 	int rc;
 	int len;
@@ -331,14 +331,7 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server)
 
 	return !rc ? -1 : 0;
 }
-#else
-static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
-{
-	return 0;
-}
-#endif
 
-#ifdef CONFIG_CIFS_DFS_UPCALL
 /* These functions must be called with server->srv_mutex held */
 static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
 				       struct cifs_sb_info *cifs_sb,
@@ -346,6 +339,7 @@ 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;
@@ -370,6 +364,12 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
 			 "%s: failed to extract hostname from target: %ld\n",
 			 __func__, PTR_ERR(server->hostname));
 	}
+
+	rc = reconn_set_ipaddr_from_hostname(server);
+	if (rc) {
+		cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
+			 __func__, rc);
+	}
 }
 
 static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
@@ -528,11 +528,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
 		 */
 		reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
 #endif
-		rc = reconn_set_ipaddr(server);
-		if (rc) {
-			cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
-				 __func__, rc);
-		}
 
 		if (cifs_rdma_enabled(server))
 			rc = smbd_reconnect(server);
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 10/11] cifs: Handle witness client move notification
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (8 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-11-30 18:02 ` [PATCH v4 11/11] cifs: Handle witness share moved notification Samuel Cabrero
  2020-12-09 12:36 ` [PATCH v4 00/11] Witness protocol support for transparent failover Aurélien Aptel
  11 siblings, 0 replies; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

This message is sent to tell a client to close its current connection
and connect to the specified address.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifs_swn.c | 140 ++++++++++++++++++++++++++++++++++++++++++++-
 fs/cifs/cifsglob.h |   4 ++
 fs/cifs/connect.c  |  26 +++++++--
 3 files changed, 162 insertions(+), 8 deletions(-)

diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
index 642c9eedc8ab..a172769c239f 100644
--- a/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -78,6 +78,7 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
 	struct sk_buff *skb;
 	struct genlmsghdr *hdr;
 	enum securityEnum authtype;
+	struct sockaddr_storage *addr;
 	int ret;
 
 	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -104,8 +105,18 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
 	if (ret < 0)
 		goto nlmsg_fail;
 
-	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
-			&swnreg->tcon->ses->server->dstaddr);
+	/*
+	 * If there is an address stored use it instead of the server address, because we are
+	 * in the process of reconnecting to it after a share has been moved or we have been
+	 * told to switch to it (client move message). In these cases we unregister from the
+	 * server address and register to the new address when we receive the notification.
+	 */
+	if (swnreg->tcon->ses->server->use_swn_dstaddr)
+		addr = &swnreg->tcon->ses->server->swn_dstaddr;
+	else
+		addr = &swnreg->tcon->ses->server->dstaddr;
+
+	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
 	if (ret < 0)
 		goto nlmsg_fail;
 
@@ -413,6 +424,120 @@ static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const ch
 	return 0;
 }
 
+static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
+{
+	if (addr1->ss_family != addr2->ss_family)
+		return false;
+
+	if (addr1->ss_family == AF_INET) {
+		return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr,
+				&((const struct sockaddr_in *)addr2)->sin_addr,
+				sizeof(struct in_addr)) == 0);
+	}
+
+	if (addr1->ss_family == AF_INET6) {
+		return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr,
+				&((const struct sockaddr_in6 *)addr2)->sin6_addr,
+				sizeof(struct in6_addr)) == 0);
+	}
+
+	return false;
+}
+
+static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new,
+				   const struct sockaddr_storage *old,
+				   struct sockaddr_storage *dst)
+{
+	__be16 port;
+
+	if (old->ss_family == AF_INET) {
+		struct sockaddr_in *ipv4 = (struct sockaddr_in *)old;
+
+		port = ipv4->sin_port;
+	}
+
+	if (old->ss_family == AF_INET6) {
+		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old;
+
+		port = ipv6->sin6_port;
+	}
+
+	if (new->ss_family == AF_INET) {
+		struct sockaddr_in *ipv4 = (struct sockaddr_in *)new;
+
+		ipv4->sin_port = port;
+	}
+
+	if (new->ss_family == AF_INET6) {
+		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new;
+
+		ipv6->sin6_port = port;
+	}
+
+	*dst = *new;
+
+	return 0;
+}
+
+static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
+{
+	/* Store the reconnect address */
+	mutex_lock(&tcon->ses->server->srv_mutex);
+	if (!cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) {
+		int ret;
+
+		ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr,
+				&tcon->ses->server->swn_dstaddr);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret);
+			return ret;
+		}
+		tcon->ses->server->use_swn_dstaddr = true;
+
+		/*
+		 * Unregister to stop receiving notifications for the old IP address.
+		 */
+		ret = cifs_swn_unregister(tcon);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
+					__func__, ret);
+			return ret;
+		}
+
+		/*
+		 * And register to receive notifications for the new IP address now that we have
+		 * stored the new address.
+		 */
+		ret = cifs_swn_register(tcon);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n",
+					__func__, ret);
+			return ret;
+		}
+
+		spin_lock(&GlobalMid_Lock);
+		if (tcon->ses->server->tcpStatus != CifsExiting)
+			tcon->ses->server->tcpStatus = CifsNeedReconnect;
+		spin_unlock(&GlobalMid_Lock);
+	}
+	mutex_unlock(&tcon->ses->server->srv_mutex);
+
+	return 0;
+}
+
+static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
+{
+	struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
+	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
+
+	if (addr->ss_family == AF_INET)
+		cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr);
+	else if (addr->ss_family == AF_INET6)
+		cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
+
+	return cifs_swn_reconnect(swnreg->tcon, addr);
+}
+
 int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cifs_swn_reg *swnreg;
@@ -461,6 +586,17 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
 		}
 		return cifs_swn_resource_state_changed(swnreg, name, state);
 	}
+	case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
+		struct sockaddr_storage addr;
+
+		if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) {
+			nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
+		} else {
+			cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
+			return -EINVAL;
+		}
+		return cifs_swn_client_move(swnreg, &addr);
+	}
 	default:
 		cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
 		break;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index f45b7c0fbceb..d7f43cec0f13 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -779,6 +779,10 @@ struct TCP_Server_Info {
 	int nr_targets;
 	bool noblockcnt; /* use non-blocking connect() */
 	bool is_channel; /* if a session channel */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	bool use_swn_dstaddr;
+	struct sockaddr_storage swn_dstaddr;
+#endif
 };
 
 struct cifs_credits {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 3af88711643b..6bfa232712a5 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -520,13 +520,24 @@ cifs_reconnect(struct TCP_Server_Info *server)
 		try_to_freeze();
 
 		mutex_lock(&server->srv_mutex);
+
+#ifdef CONFIG_CIFS_SWN_UPCALL
+		if (server->use_swn_dstaddr) {
+			server->dstaddr = server->swn_dstaddr;
+		} else {
+#endif
+
 #ifdef CONFIG_CIFS_DFS_UPCALL
-		/*
-		 * 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);
+			/*
+			 * 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);
+#endif
+
+#ifdef CONFIG_CIFS_SWN_UPCALL
+		}
 #endif
 
 		if (cifs_rdma_enabled(server))
@@ -544,6 +555,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
 			if (server->tcpStatus != CifsExiting)
 				server->tcpStatus = CifsNeedNegotiate;
 			spin_unlock(&GlobalMid_Lock);
+#ifdef CONFIG_CIFS_SWN_UPCALL
+			server->use_swn_dstaddr = false;
+#endif
 			mutex_unlock(&server->srv_mutex);
 		}
 	} while (server->tcpStatus == CifsNeedReconnect);
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v4 11/11] cifs: Handle witness share moved notification
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (9 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 10/11] cifs: Handle witness client move notification Samuel Cabrero
@ 2020-11-30 18:02 ` Samuel Cabrero
  2020-12-09 12:36 ` [PATCH v4 00/11] Witness protocol support for transparent failover Aurélien Aptel
  11 siblings, 0 replies; 23+ messages in thread
From: Samuel Cabrero @ 2020-11-30 18:02 UTC (permalink / raw)
  To: linux-cifs; +Cc: Samuel Cabrero

Just log a message as it is not clear enough what this notification
means. It is received for example when a node belonging to a scale-out
file server dies, but it is also received as soon as a client registers
for notifications (w2k12r2) when the server is scale-out.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/cifs_swn.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
index a172769c239f..c505d35f5b57 100644
--- a/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -538,6 +538,29 @@ static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_sto
 	return cifs_swn_reconnect(swnreg->tcon, addr);
 }
 
+static int cifs_swn_share_moved(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
+{
+	struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
+	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
+
+	if (addr->ss_family == AF_INET)
+		cifs_dbg(FYI, "%s: share '%s' from '%s' moved to %pI4\n",
+				__func__, swnreg->share_name, swnreg->net_name,
+				&ipv4->sin_addr);
+	else if (addr->ss_family == AF_INET6)
+		cifs_dbg(FYI, "%s: share '%s' from '%s' moved to %pI6\n",
+				__func__, swnreg->share_name, swnreg->net_name,
+				&ipv6->sin6_addr);
+
+	/*
+	 * FIXME It is not clear enough how to handle this notification and what does it mean.
+	 * It is received for example when a node belonging to a scale-out file server dies, but
+	 * it is also received as soon as a client registers for notifications (w2k12r2) when the
+	 * share is scale-out and all nodes are working fine. Just log for now.
+	 */
+	return 0;
+}
+
 int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cifs_swn_reg *swnreg;
@@ -597,6 +620,17 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
 		}
 		return cifs_swn_client_move(swnreg, &addr);
 	}
+	case CIFS_SWN_NOTIFICATION_SHARE_MOVE: {
+		struct sockaddr_storage addr;
+
+		if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) {
+			nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
+		} else {
+			cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
+			return -EINVAL;
+		}
+		return cifs_swn_share_moved(swnreg, &addr);
+	}
 	default:
 		cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
 		break;
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 00/11] Witness protocol support for transparent failover
  2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
                   ` (10 preceding siblings ...)
  2020-11-30 18:02 ` [PATCH v4 11/11] cifs: Handle witness share moved notification Samuel Cabrero
@ 2020-12-09 12:36 ` Aurélien Aptel
  2020-12-09 15:06   ` Paulo Alcantara
  11 siblings, 1 reply; 23+ messages in thread
From: Aurélien Aptel @ 2020-12-09 12:36 UTC (permalink / raw)
  To: scabrero, linux-cifs, Steve French, Paulo Alcantara

Hi,

I've added a "failover" test group to the buildbot that mounts a
"regular" (non-scaleout) cluster and switches the fileserver to another
cluster node live and it looks like it's working: you can keep on using
the mount.

In non-scale-out, the file server has its own virtual IP that both node
share. So when you "move" the fileserver to a different node, it doesn't
actually change IP. After doing that we realized that this actually
works already without -o witness since it's reconnecting to the same IP.

Now we need to add a scale-out cluster fileserver in buildbot where,
IIUC (please correct me Samuel) the fileserver is actually using the
node IP instead of this virtual-IP shared by nodes. So that when we move
the fileserver, it actually changes its IP address and we can test this
properly.

As for the code, I'm not an expert on reconnection but it looks for
merging I think. It doesn't handle multichannel but multchannel doesn't
handle reconnection well anyway. There is an issue which pops up in
other parts of the code as well.

If you run a command too quickly after the transition, they will fail
with EIO so it's not completely failing over but I think there can be
the same issue with DFS (Paulo, any ideas/comments?)  which is why we do
2 times ls and we ignore the result of the first in the DFS tests.

the dfs test code:

    def io_reco_test(unc, opts, cwd, expected):
        try:
            lsdir = '.'
            cddir = os.path.join(ARGS.mnt, cwd)
            info(("TEST: mount {unc} , cd {cddir} , ls {lsdir}, expect:[{expect}]\n"+
                  "      disconnect {cddir} , ls#1 {lsdir} (fail here is ok),  ls#2 (fail here NOT ok)").format(
                      unc=unc, cddir=cddir, lsdir=lsdir, expect=" ".join(['"%s"'%x for x in expected])
                  ))


Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 00/11] Witness protocol support for transparent failover
  2020-12-09 12:36 ` [PATCH v4 00/11] Witness protocol support for transparent failover Aurélien Aptel
@ 2020-12-09 15:06   ` Paulo Alcantara
  0 siblings, 0 replies; 23+ messages in thread
From: Paulo Alcantara @ 2020-12-09 15:06 UTC (permalink / raw)
  To: Aurélien Aptel, scabrero, linux-cifs, Steve French

Aurélien Aptel <aaptel@suse.de> writes:

> I've added a "failover" test group to the buildbot that mounts a
> "regular" (non-scaleout) cluster and switches the fileserver to another
> cluster node live and it looks like it's working: you can keep on using
> the mount.
>
> In non-scale-out, the file server has its own virtual IP that both node
> share. So when you "move" the fileserver to a different node, it doesn't
> actually change IP. After doing that we realized that this actually
> works already without -o witness since it's reconnecting to the same IP.
>
> Now we need to add a scale-out cluster fileserver in buildbot where,
> IIUC (please correct me Samuel) the fileserver is actually using the
> node IP instead of this virtual-IP shared by nodes. So that when we move
> the fileserver, it actually changes its IP address and we can test this
> properly.
>
> As for the code, I'm not an expert on reconnection but it looks for
> merging I think. It doesn't handle multichannel but multchannel doesn't
> handle reconnection well anyway. There is an issue which pops up in
> other parts of the code as well.
>
> If you run a command too quickly after the transition, they will fail
> with EIO so it's not completely failing over but I think there can be
> the same issue with DFS (Paulo, any ideas/comments?)  which is why we do
> 2 times ls and we ignore the result of the first in the DFS tests.
>
> the dfs test code:
>
>     def io_reco_test(unc, opts, cwd, expected):
>         try:
>             lsdir = '.'
>             cddir = os.path.join(ARGS.mnt, cwd)
>             info(("TEST: mount {unc} , cd {cddir} , ls {lsdir}, expect:[{expect}]\n"+
>                   "      disconnect {cddir} , ls#1 {lsdir} (fail here is ok),  ls#2 (fail here NOT ok)").format(
>                       unc=unc, cddir=cddir, lsdir=lsdir, expect=" ".join(['"%s"'%x for x in expected])
>                   ))

For soft mounts, it is OK ignoring the first ls.  But for hard mounts,
we shouldn't ignore the first ls as it must retry forever until failover
is done.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 01/11] cifs: Make extract_hostname function public
  2020-11-30 18:02 ` [PATCH v4 01/11] cifs: Make extract_hostname function public Samuel Cabrero
@ 2020-12-12  3:42   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  3:42 UTC (permalink / raw)
  To: Samuel Cabrero
  Cc: CIFS, Rafal Szczesniak, Aurélien Aptel, ronnie sahlberg

[-- Attachment #1: Type: text/plain, Size: 4647 bytes --]

I have fixed up (various merge conflicts etc.) and merged the first
three in the series (see attached), as well as added a patch for some
comment cleanups, and a patch to move (following Rafal's suggestion) a
few of the functions from fs/cifs/misc.c to a new file fs/cifs/unc.c

The first three in the series are very low risk and can be merged
independent of the others to make review/cleanup of the final patches
easier.  I will fixup patch 4 (which add a mount parm and thus
requires rebasing on Ronnie's mount API series).  Am planning
reviewing the final seven in the series, let me know if changes needed
to the remaining (ie patches 5 through 11).


On Mon, Nov 30, 2020 at 12:04 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> Move the function to misc.c and give it a public header.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cifsproto.h |  1 +
>  fs/cifs/connect.c   | 34 ----------------------------------
>  fs/cifs/misc.c      | 32 ++++++++++++++++++++++++++++++++
>  3 files changed, 33 insertions(+), 34 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 24c6f36177ba..d716e81d86fa 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -620,6 +620,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
>  struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
>  void cifs_put_tcp_super(struct super_block *sb);
>  int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
> +char *extract_hostname(const char *unc);
>
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>  static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 28c1459fb0fc..a938371af6ef 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -284,7 +284,6 @@ 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);
> -static char *extract_hostname(const char *unc);
>
>  /*
>   * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
> @@ -1232,39 +1231,6 @@ cifs_demultiplex_thread(void *p)
>         module_put_and_exit(0);
>  }
>
> -/* extract the host portion of the UNC string */
> -static char *
> -extract_hostname(const char *unc)
> -{
> -       const char *src;
> -       char *dst, *delim;
> -       unsigned int len;
> -
> -       /* skip double chars at beginning of string */
> -       /* BB: check validity of these bytes? */
> -       if (strlen(unc) < 3)
> -               return ERR_PTR(-EINVAL);
> -       for (src = unc; *src && *src == '\\'; src++)
> -               ;
> -       if (!*src)
> -               return ERR_PTR(-EINVAL);
> -
> -       /* delimiter between hostname and sharename is always '\\' now */
> -       delim = strchr(src, '\\');
> -       if (!delim)
> -               return ERR_PTR(-EINVAL);
> -
> -       len = delim - src;
> -       dst = kmalloc((len + 1), GFP_KERNEL);
> -       if (dst == NULL)
> -               return ERR_PTR(-ENOMEM);
> -
> -       memcpy(dst, src, len);
> -       dst[len] = '\0';
> -
> -       return dst;
> -}
> -
>  static int get_option_ul(substring_t args[], unsigned long *option)
>  {
>         int rc;
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 1c14cf01dbef..3d5cc25c167f 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -1195,3 +1195,35 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
>         cifs_put_tcon_super(sb);
>         return rc;
>  }
> +
> +/* extract the host portion of the UNC string */
> +char *extract_hostname(const char *unc)
> +{
> +       const char *src;
> +       char *dst, *delim;
> +       unsigned int len;
> +
> +       /* skip double chars at beginning of string */
> +       /* BB: check validity of these bytes? */
> +       if (strlen(unc) < 3)
> +               return ERR_PTR(-EINVAL);
> +       for (src = unc; *src && *src == '\\'; src++)
> +               ;
> +       if (!*src)
> +               return ERR_PTR(-EINVAL);
> +
> +       /* delimiter between hostname and sharename is always '\\' now */
> +       delim = strchr(src, '\\');
> +       if (!delim)
> +               return ERR_PTR(-EINVAL);
> +
> +       len = delim - src;
> +       dst = kmalloc((len + 1), GFP_KERNEL);
> +       if (dst == NULL)
> +               return ERR_PTR(-ENOMEM);
> +
> +       memcpy(dst, src, len);
> +       dst[len] = '\0';
> +
> +       return dst;
> +}
> --
> 2.29.2
>


--
Thanks,

Steve

[-- Attachment #2: 0002-cifs-Make-extract_sharename-function-public.patch --]
[-- Type: text/x-patch, Size: 3351 bytes --]

From 749765e260720d332eb8f03c3be28557b8ceb954 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:48 +0100
Subject: [PATCH 2/5] cifs: Make extract_sharename function public

Move the function to misc.c

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cache.c     | 24 ------------------------
 fs/cifs/cifsproto.h |  1 +
 fs/cifs/fscache.c   |  1 +
 fs/cifs/fscache.h   |  1 -
 fs/cifs/misc.c      | 24 ++++++++++++++++++++++++
 5 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
index 0f2adecb94f2..488fe0ffc1ef 100644
--- a/fs/cifs/cache.c
+++ b/fs/cifs/cache.c
@@ -53,30 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = {
 	.type = FSCACHE_COOKIE_TYPE_INDEX,
 };
 
-char *extract_sharename(const char *treename)
-{
-	const char *src;
-	char *delim, *dst;
-	int len;
-
-	/* skip double chars at the beginning */
-	src = treename + 2;
-
-	/* share name is always preceded by '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-	delim++;
-	len = strlen(delim);
-
-	/* caller has to free the memory */
-	dst = kstrndup(delim, len, GFP_KERNEL);
-	if (!dst)
-		return ERR_PTR(-ENOMEM);
-
-	return dst;
-}
-
 static enum
 fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
 					      const void *data,
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 3fe0c4a0d36d..b80b57a66804 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -618,6 +618,7 @@ struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
 int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
 char *extract_hostname(const char *unc);
+char *extract_sharename(const char *unc);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index da688185403c..20d24af33ee2 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -22,6 +22,7 @@
 #include "cifsglob.h"
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
+#include "cifsproto.h"
 
 /*
  * Key layout of CIFS server cache index object
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
index 1091633d2adb..e811f2dd7619 100644
--- a/fs/cifs/fscache.h
+++ b/fs/cifs/fscache.h
@@ -57,7 +57,6 @@ extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
 
 extern int cifs_fscache_register(void);
 extern void cifs_fscache_unregister(void);
-extern char *extract_sharename(const char *);
 
 /*
  * fscache.c
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 3d5cc25c167f..f0a1c24751b2 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1227,3 +1227,27 @@ char *extract_hostname(const char *unc)
 
 	return dst;
 }
+
+char *extract_sharename(const char *unc)
+{
+	const char *src;
+	char *delim, *dst;
+	int len;
+
+	/* skip double chars at the beginning */
+	src = unc + 2;
+
+	/* share name is always preceded by '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+	delim++;
+	len = strlen(delim);
+
+	/* caller has to free the memory */
+	dst = kstrndup(delim, len, GFP_KERNEL);
+	if (!dst)
+		return ERR_PTR(-ENOMEM);
+
+	return dst;
+}
-- 
2.27.0


[-- Attachment #3: 0003-cifs-minor-kernel-style-fixes-for-comments.patch --]
[-- Type: text/x-patch, Size: 3231 bytes --]

From bcbee8cbe271b8af465cdc66efe9c1620ea25dbb Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Fri, 11 Dec 2020 19:48:26 -0600
Subject: [PATCH 3/5] cifs: minor kernel style fixes for comments

Trivial fix for a few comments which didn't follow kernel style

Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/connect.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index d3836db33e7c..ec80b6c3e20f 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1024,9 +1024,9 @@ cifs_demultiplex_thread(void *p)
 	module_put_and_exit(0);
 }
 
-/** Returns true if srcaddr isn't specified and rhs isn't
- * specified, or if srcaddr is specified and
- * matches the IP address of the rhs argument.
+/**
+ * Returns true if srcaddr isn't specified and rhs isn't specified, or
+ * if srcaddr is specified and matches the IP address of the rhs argument
  */
 bool
 cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
@@ -2544,7 +2544,8 @@ ip_connect(struct TCP_Server_Info *server)
 void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
 			  struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
 {
-	/* if we are reconnecting then should we check to see if
+	/*
+	 * If we are reconnecting then should we check to see if
 	 * any requested capabilities changed locally e.g. via
 	 * remount but we can not do much about it here
 	 * if they have (even if we could detect it by the following)
@@ -2552,7 +2553,8 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
 	 * or if we change to make all sb to same share the same
 	 * sb as NFS - then we only have one backpointer to sb.
 	 * What if we wanted to mount the server share twice once with
-	 * and once without posixacls or posix paths? */
+	 * and once without posixacls or posix paths?
+	 */
 	__u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
 
 	if (ctx && ctx->no_linux_ext) {
@@ -2571,11 +2573,15 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
 	if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
 		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
 		cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
-		/* check for reconnect case in which we do not
-		   want to change the mount behavior if we can avoid it */
+		/*
+		 * check for reconnect case in which we do not
+		 * want to change the mount behavior if we can avoid it
+		 */
 		if (ctx == NULL) {
-			/* turn off POSIX ACL and PATHNAMES if not set
-			   originally at mount time */
+			/*
+			 * turn off POSIX ACL and PATHNAMES if not set
+			 * originally at mount time
+			 */
 			if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
 				cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
 			if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
@@ -2977,7 +2983,6 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx,
 /**
  * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb
  *
- *
  * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated
  * to a string containing updated options for the submount.  Otherwise it
  * will be left untouched.
-- 
2.27.0


[-- Attachment #4: 0004-cifs-cleanup-misc.c.patch --]
[-- Type: text/x-patch, Size: 4447 bytes --]

From b69c063066231af65879321fd53cd371c106fa82 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Fri, 11 Dec 2020 20:22:04 -0600
Subject: [PATCH 4/5] cifs: cleanup misc.c

misc.c was getting a little large, move two of the UNC parsing relating
functions to a new C file unc.c which makes the coding of the
upcoming witness protocol patch series a little cleaner as well.

Suggested-by: Rafal Szczesniak <rafal@elbingbrewery.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/Makefile |  2 +-
 fs/cifs/misc.c   | 56 ----------------------------------------
 fs/cifs/unc.c    | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+), 57 deletions(-)
 create mode 100644 fs/cifs/unc.c

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index cd17d0e50f2a..848ebad6af7d 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
 	  inode.o link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
 	  cifs_unicode.o nterr.o cifsencrypt.o \
-	  readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o \
+	  readdir.o ioctl.o sess.o export.o smb1ops.o unc.o winucase.o \
 	  smb2ops.o smb2maperror.o smb2transport.o \
 	  smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o
 
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index f0a1c24751b2..1c14cf01dbef 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1195,59 +1195,3 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
 	cifs_put_tcon_super(sb);
 	return rc;
 }
-
-/* extract the host portion of the UNC string */
-char *extract_hostname(const char *unc)
-{
-	const char *src;
-	char *dst, *delim;
-	unsigned int len;
-
-	/* skip double chars at beginning of string */
-	/* BB: check validity of these bytes? */
-	if (strlen(unc) < 3)
-		return ERR_PTR(-EINVAL);
-	for (src = unc; *src && *src == '\\'; src++)
-		;
-	if (!*src)
-		return ERR_PTR(-EINVAL);
-
-	/* delimiter between hostname and sharename is always '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-
-	len = delim - src;
-	dst = kmalloc((len + 1), GFP_KERNEL);
-	if (dst == NULL)
-		return ERR_PTR(-ENOMEM);
-
-	memcpy(dst, src, len);
-	dst[len] = '\0';
-
-	return dst;
-}
-
-char *extract_sharename(const char *unc)
-{
-	const char *src;
-	char *delim, *dst;
-	int len;
-
-	/* skip double chars at the beginning */
-	src = unc + 2;
-
-	/* share name is always preceded by '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-	delim++;
-	len = strlen(delim);
-
-	/* caller has to free the memory */
-	dst = kstrndup(delim, len, GFP_KERNEL);
-	if (!dst)
-		return ERR_PTR(-ENOMEM);
-
-	return dst;
-}
diff --git a/fs/cifs/unc.c b/fs/cifs/unc.c
new file mode 100644
index 000000000000..2c5665f5543a
--- /dev/null
+++ b/fs/cifs/unc.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *   Copyright (C) 2020, Microsoft Corporation.
+ *
+ *   Author(s): Steve French <stfrench@microsoft.com>
+ *              Suresh Jayaraman <sjayaraman@suse.de>
+ *              Jeff Layton <jlayton@kernel.org>
+ */
+
+#include <linux/slab.h>
+#include "cifsproto.h"
+
+/* extract the host portion of the UNC string */
+char *extract_hostname(const char *unc)
+{
+	const char *src;
+	char *dst, *delim;
+	unsigned int len;
+
+	/* skip double chars at beginning of string */
+	/* BB: check validity of these bytes? */
+	if (strlen(unc) < 3)
+		return ERR_PTR(-EINVAL);
+	for (src = unc; *src && *src == '\\'; src++)
+		;
+	if (!*src)
+		return ERR_PTR(-EINVAL);
+
+	/* delimiter between hostname and sharename is always '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+
+	len = delim - src;
+	dst = kmalloc((len + 1), GFP_KERNEL);
+	if (dst == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(dst, src, len);
+	dst[len] = '\0';
+
+	return dst;
+}
+
+char *extract_sharename(const char *unc)
+{
+	const char *src;
+	char *delim, *dst;
+	int len;
+
+	/* skip double chars at the beginning */
+	src = unc + 2;
+
+	/* share name is always preceded by '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+	delim++;
+	len = strlen(delim);
+
+	/* caller has to free the memory */
+	dst = kstrndup(delim, len, GFP_KERNEL);
+	if (!dst)
+		return ERR_PTR(-ENOMEM);
+
+	return dst;
+}
-- 
2.27.0


[-- Attachment #5: 0005-cifs-Register-generic-netlink-family.patch --]
[-- Type: text/x-patch, Size: 6444 bytes --]

From cf6a6c0841bc125a9ede50000c22f0110eea805b Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:49 +0100
Subject: [PATCH 5/5] cifs: Register generic netlink family

Register a new generic netlink family to talk to the witness service
userspace daemon.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/Kconfig                        | 11 ++++
 fs/cifs/Makefile                       |  2 +
 fs/cifs/cifsfs.c                       | 17 ++++++-
 fs/cifs/netlink.c                      | 69 ++++++++++++++++++++++++++
 fs/cifs/netlink.h                      | 16 ++++++
 include/uapi/linux/cifs/cifs_netlink.h | 31 ++++++++++++
 6 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 fs/cifs/netlink.c
 create mode 100644 fs/cifs/netlink.h
 create mode 100644 include/uapi/linux/cifs/cifs_netlink.h

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 604f65f4b6c5..664ac5c63d39 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -190,6 +190,17 @@ config CIFS_DFS_UPCALL
 	  servers if their addresses change or for implicit mounts of
 	  DFS junction points. If unsure, say Y.
 
+config CIFS_SWN_UPCALL
+	bool "SWN feature support"
+	depends on CIFS
+	help
+	  The Service Witness Protocol (SWN) is used to get notifications
+	  from a highly available server of resource state changes. This
+	  feature enables an upcall mechanism for CIFS which contacts an
+	  userspace daemon to establish the DCE/RPC connection to retrieve
+	  the cluster available interfaces and resource change notifications.
+	  If unsure, say Y.
+
 config CIFS_NFSD_EXPORT
 	bool "Allow nfsd to export CIFS file system"
 	depends on CIFS && BROKEN
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 848ebad6af7d..9e398d227b0e 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -18,6 +18,8 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
 
+cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
+
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
 
 cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 4f27f77d3053..5d32561ae2ed 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -55,6 +55,9 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "netlink.h"
+#endif
 #include "fs_context.h"
 
 /*
@@ -1601,10 +1604,15 @@ init_cifs(void)
 	if (rc)
 		goto out_destroy_dfs_cache;
 #endif /* CONFIG_CIFS_UPCALL */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	rc = cifs_genl_init();
+	if (rc)
+		goto out_register_key_type;
+#endif /* CONFIG_CIFS_SWN_UPCALL */
 
 	rc = init_cifs_idmap();
 	if (rc)
-		goto out_register_key_type;
+		goto out_cifs_swn_init;
 
 	rc = register_filesystem(&cifs_fs_type);
 	if (rc)
@@ -1620,7 +1628,11 @@ init_cifs(void)
 
 out_init_cifs_idmap:
 	exit_cifs_idmap();
+out_cifs_swn_init:
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
 out_register_key_type:
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 out_destroy_dfs_cache:
@@ -1657,6 +1669,9 @@ exit_cifs(void)
 	unregister_filesystem(&smb3_fs_type);
 	cifs_dfs_release_automount_timer();
 	exit_cifs_idmap();
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 #endif
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
new file mode 100644
index 000000000000..b9154661fa85
--- /dev/null
+++ b/fs/cifs/netlink.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#include <net/genetlink.h>
+#include <uapi/linux/cifs/cifs_netlink.h>
+
+#include "netlink.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+
+static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
+};
+
+static struct genl_ops cifs_genl_ops[] = {
+};
+
+static const struct genl_multicast_group cifs_genl_mcgrps[] = {
+	[CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
+};
+
+struct genl_family cifs_genl_family = {
+	.name		= CIFS_GENL_NAME,
+	.version	= CIFS_GENL_VERSION,
+	.hdrsize	= 0,
+	.maxattr	= CIFS_GENL_ATTR_MAX,
+	.module		= THIS_MODULE,
+	.policy		= cifs_genl_policy,
+	.ops		= cifs_genl_ops,
+	.n_ops		= ARRAY_SIZE(cifs_genl_ops),
+	.mcgrps		= cifs_genl_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(cifs_genl_mcgrps),
+};
+
+/**
+ * cifs_genl_init - Register generic netlink family
+ *
+ * Return zero if initialized successfully, otherwise non-zero.
+ */
+int cifs_genl_init(void)
+{
+	int ret;
+
+	ret = genl_register_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to register netlink family\n",
+				__func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * cifs_genl_exit - Unregister generic netlink family
+ */
+void cifs_genl_exit(void)
+{
+	int ret;
+
+	ret = genl_unregister_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to unregister netlink family\n",
+				__func__);
+	}
+}
diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h
new file mode 100644
index 000000000000..e2fa8ed24c54
--- /dev/null
+++ b/fs/cifs/netlink.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#ifndef _CIFS_NETLINK_H
+#define _CIFS_NETLINK_H
+
+extern struct genl_family cifs_genl_family;
+
+extern int cifs_genl_init(void);
+extern void cifs_genl_exit(void);
+
+#endif /* _CIFS_NETLINK_H */
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
new file mode 100644
index 000000000000..cdb1bd78fbc7
--- /dev/null
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+
+#ifndef _UAPILINUX_CIFS_NETLINK_H
+#define _UAPILINUX_CIFS_NETLINK_H
+
+#define CIFS_GENL_NAME			"cifs"
+#define CIFS_GENL_VERSION		0x1
+
+#define CIFS_GENL_MCGRP_SWN_NAME	"cifs_mcgrp_swn"
+
+enum cifs_genl_multicast_groups {
+	CIFS_GENL_MCGRP_SWN,
+};
+
+enum cifs_genl_attributes {
+	__CIFS_GENL_ATTR_MAX,
+};
+#define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
+
+enum cifs_genl_commands {
+	__CIFS_GENL_CMD_MAX
+};
+#define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
+
+#endif /* _UAPILINUX_CIFS_NETLINK_H */
-- 
2.27.0


[-- Attachment #6: 0001-cifs-Make-extract_hostname-function-public.patch --]
[-- Type: text/x-patch, Size: 3651 bytes --]

From 855b7b6a65b954944f2f8801925196fa62599528 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:47 +0100
Subject: [PATCH 1/5] cifs: Make extract_hostname function public

Move the function to misc.c and give it a public header.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cifsproto.h |  1 +
 fs/cifs/connect.c   | 34 ----------------------------------
 fs/cifs/misc.c      | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index aa7a717c34ab..3fe0c4a0d36d 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -617,6 +617,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
 struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
 int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
+char *extract_hostname(const char *unc);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 95b12f148735..d3836db33e7c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -77,7 +77,6 @@ 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);
-static char *extract_hostname(const char *unc);
 
 /*
  * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
@@ -1025,39 +1024,6 @@ cifs_demultiplex_thread(void *p)
 	module_put_and_exit(0);
 }
 
-/* extract the host portion of the UNC string */
-static char *
-extract_hostname(const char *unc)
-{
-	const char *src;
-	char *dst, *delim;
-	unsigned int len;
-
-	/* skip double chars at beginning of string */
-	/* BB: check validity of these bytes? */
-	if (strlen(unc) < 3)
-		return ERR_PTR(-EINVAL);
-	for (src = unc; *src && *src == '\\'; src++)
-		;
-	if (!*src)
-		return ERR_PTR(-EINVAL);
-
-	/* delimiter between hostname and sharename is always '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-
-	len = delim - src;
-	dst = kmalloc((len + 1), GFP_KERNEL);
-	if (dst == NULL)
-		return ERR_PTR(-ENOMEM);
-
-	memcpy(dst, src, len);
-	dst[len] = '\0';
-
-	return dst;
-}
-
 /** Returns true if srcaddr isn't specified and rhs isn't
  * specified, or if srcaddr is specified and
  * matches the IP address of the rhs argument.
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 1c14cf01dbef..3d5cc25c167f 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1195,3 +1195,35 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
 	cifs_put_tcon_super(sb);
 	return rc;
 }
+
+/* extract the host portion of the UNC string */
+char *extract_hostname(const char *unc)
+{
+	const char *src;
+	char *dst, *delim;
+	unsigned int len;
+
+	/* skip double chars at beginning of string */
+	/* BB: check validity of these bytes? */
+	if (strlen(unc) < 3)
+		return ERR_PTR(-EINVAL);
+	for (src = unc; *src && *src == '\\'; src++)
+		;
+	if (!*src)
+		return ERR_PTR(-EINVAL);
+
+	/* delimiter between hostname and sharename is always '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+
+	len = delim - src;
+	dst = kmalloc((len + 1), GFP_KERNEL);
+	if (dst == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(dst, src, len);
+	dst[len] = '\0';
+
+	return dst;
+}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 04/11] cifs: add witness mount option and data structs
  2020-11-30 18:02 ` [PATCH v4 04/11] cifs: add witness mount option and data structs Samuel Cabrero
@ 2020-12-12  5:07   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:07 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS, Aurélien Aptel, ronnie sahlberg

[-- Attachment #1: Type: text/plain, Size: 5003 bytes --]

Updated to support new mount API (rebased on Ronnie's patch series).
Let me know if you see any problems.


On Mon, Nov 30, 2020 at 12:04 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> Add 'witness' mount option to register for witness notifications.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cifsfs.c   |  5 +++++
>  fs/cifs/cifsglob.h |  4 ++++
>  fs/cifs/connect.c  | 35 ++++++++++++++++++++++++++++++++++-
>  3 files changed, 43 insertions(+), 1 deletion(-)
>
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 8111d0109a2e..c2bbc444b463 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -637,6 +637,11 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
>                 seq_printf(s, ",multichannel,max_channels=%zu",
>                            tcon->ses->chan_max);
>
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       if (tcon->use_witness)
> +               seq_puts(s, ",witness");
> +#endif
> +
>         return 0;
>  }
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 484ec2d8c5c9..f45b7c0fbceb 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -619,6 +619,7 @@ struct smb_vol {
>         unsigned int max_channels;
>         __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
>         bool rootfs:1; /* if it's a SMB root file system */
> +       bool witness:1; /* use witness protocol */
>  };
>
>  /**
> @@ -1177,6 +1178,9 @@ struct cifs_tcon {
>         int remap:2;
>         struct list_head ulist; /* cache update list */
>  #endif
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       bool use_witness:1; /* use witness protocol */
> +#endif
>  };
>
>  /*
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index a938371af6ef..22d46c8acc7f 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -102,7 +102,7 @@ enum {
>         Opt_resilient, Opt_noresilient,
>         Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
>         Opt_multichannel, Opt_nomultichannel,
> -       Opt_compress,
> +       Opt_compress, Opt_witness,
>
>         /* Mount options which take numeric value */
>         Opt_backupuid, Opt_backupgid, Opt_uid,
> @@ -276,6 +276,7 @@ static const match_table_t cifs_mount_option_tokens = {
>         { Opt_ignore, "relatime" },
>         { Opt_ignore, "_netdev" },
>         { Opt_rootfs, "rootfs" },
> +       { Opt_witness, "witness" },
>
>         { Opt_err, NULL }
>  };
> @@ -1540,6 +1541,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
>                         vol->rootfs = true;
>  #endif
>                         break;
> +               case Opt_witness:
> +#ifndef CONFIG_CIFS_SWN_UPCALL
> +                       cifs_dbg(VFS, "Witness support needs CONFIG_CIFS_SWN_UPCALL kernel config option set\n");
> +                       goto cifs_parse_mount_err;
> +#endif
> +                       vol->witness = true;
> +                       break;
>                 case Opt_posixpaths:
>                         vol->posix_paths = 1;
>                         break;
> @@ -3160,6 +3168,8 @@ cifs_put_tcon(struct cifs_tcon *tcon)
>                 return;
>         }
>
> +       /* TODO witness unregister */
> +
>         list_del_init(&tcon->tcon_list);
>         spin_unlock(&cifs_tcp_ses_lock);
>
> @@ -3321,6 +3331,26 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
>                 tcon->use_resilient = true;
>         }
>
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       tcon->use_witness = false;
> +       if (volume_info->witness) {
> +               if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
> +                       if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
> +                               /* TODO witness register */
> +                               tcon->use_witness = true;
> +                       } else {
> +                               cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
> +                               rc = -EOPNOTSUPP;
> +                               goto out_fail;
> +                       }
> +               } else {
> +                       cifs_dbg(VFS, "SMB3 or later required for witness option\n");
> +                       rc = -EOPNOTSUPP;
> +                       goto out_fail;
> +               }
> +       }
> +#endif
> +
>         /* If the user really knows what they are doing they can override */
>         if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
>                 if (volume_info->cache_ro)
> @@ -5072,6 +5102,9 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
>         vol_info->sectype = master_tcon->ses->sectype;
>         vol_info->sign = master_tcon->ses->sign;
>         vol_info->seal = master_tcon->seal;
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       vol_info->witness = master_tcon->use_witness;
> +#endif
>
>         rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
>         if (rc) {
> --
> 2.29.2
>


-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-add-witness-mount-option-and-data-structs.patch --]
[-- Type: text/x-patch, Size: 4453 bytes --]

From 11de8d5dc0521d7e6782fac8605bb7646d66d7e3 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Fri, 11 Dec 2020 22:59:29 -0600
Subject: [PATCH] cifs: add witness mount option and data structs

Add 'witness' mount option to register for witness notifications.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cifsfs.c     |  5 +++++
 fs/cifs/cifsglob.h   |  3 +++
 fs/cifs/connect.c    | 25 +++++++++++++++++++++++++
 fs/cifs/fs_context.c |  8 ++++++++
 fs/cifs/fs_context.h |  2 ++
 5 files changed, 43 insertions(+)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5d32561ae2ed..f810b25dfeb8 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -638,6 +638,11 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 		seq_printf(s, ",multichannel,max_channels=%zu",
 			   tcon->ses->chan_max);
 
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	if (tcon->use_witness)
+		seq_puts(s, ",witness");
+#endif
+
 	return 0;
 }
 
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index b46809260e79..78438102f091 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1086,6 +1086,9 @@ struct cifs_tcon {
 	int remap:2;
 	struct list_head ulist; /* cache update list */
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	bool use_witness:1; /* use witness protocol */
+#endif
 };
 
 /*
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index ec80b6c3e20f..46d4f5469688 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1944,6 +1944,8 @@ cifs_put_tcon(struct cifs_tcon *tcon)
 		return;
 	}
 
+	/* TODO witness unregister */
+
 	list_del_init(&tcon->tcon_list);
 	spin_unlock(&cifs_tcp_ses_lock);
 
@@ -2104,6 +2106,26 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
 		}
 		tcon->use_resilient = true;
 	}
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	tcon->use_witness = false;
+	if (ctx->witness) {
+		if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
+			if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
+				/* TODO witness register */
+				tcon->use_witness = true;
+			} else {
+				/* TODO: try to extend for non-cluster uses (eg multichannel) */
+				cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
+				rc = -EOPNOTSUPP;
+				goto out_fail;
+			}
+		} else {
+			cifs_dbg(VFS, "SMB3 or later required for witness option\n");
+			rc = -EOPNOTSUPP;
+			goto out_fail;
+		}
+	}
+#endif
 
 	/* If the user really knows what they are doing they can override */
 	if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
@@ -3856,6 +3878,9 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
 	ctx->sectype = master_tcon->ses->sectype;
 	ctx->sign = master_tcon->ses->sign;
 	ctx->seal = master_tcon->seal;
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	ctx->witness = master_tcon->use_witness;
+#endif
 
 	rc = cifs_set_vol_auth(ctx, master_tcon->ses);
 	if (rc) {
diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c
index 9120d148c5f1..fe5cc60f4393 100644
--- a/fs/cifs/fs_context.c
+++ b/fs/cifs/fs_context.c
@@ -119,6 +119,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
 	fsparam_flag("modesid", Opt_modesid),
 	fsparam_flag("rootfs", Opt_rootfs),
 	fsparam_flag("compress", Opt_compress),
+	fsparam_flag("witness", Opt_witness),
 
 	/* Mount options which take numeric value */
 	fsparam_u32("backupuid", Opt_backupuid),
@@ -1004,6 +1005,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
 		if (cifs_parse_cache_flavor(param->string, ctx) != 0)
 			goto cifs_parse_mount_err;
 		break;
+	case Opt_witness:
+#ifndef CONFIG_CIFS_SWN_UPCALL
+		cifs_dbg(VFS, "Witness support needs CONFIG_CIFS_SWN_UPCALL config option\n");
+			goto cifs_parse_mount_err;
+#endif
+		ctx->witness = true;
+		break;
 	case Opt_rootfs:
 #ifdef CONFIG_CIFS_ROOT
 		ctx->rootfs = true;
diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h
index d4a905a80883..aaec8a819d34 100644
--- a/fs/cifs/fs_context.h
+++ b/fs/cifs/fs_context.h
@@ -102,6 +102,7 @@ enum cifs_param {
 	Opt_rootfs,
 	Opt_multichannel,
 	Opt_compress,
+	Opt_witness,
 
 	/* Mount options which take numeric value */
 	Opt_backupuid,
@@ -241,6 +242,7 @@ struct smb3_fs_context {
 	unsigned int max_channels;
 	__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
 	bool rootfs:1; /* if it's a SMB root file system */
+	bool witness:1; /* use witness protocol */
 
 	char *mount_options;
 };
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 02/11] cifs: Make extract_sharename function public
  2020-11-30 18:02 ` [PATCH v4 02/11] cifs: Make extract_sharename " Samuel Cabrero
@ 2020-12-12  5:50   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:50 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

[-- Attachment #1: Type: text/plain, Size: 3778 bytes --]

updated version of the patch attached (rebased on current for-next).
Tenatively merged into cifs-2.6.git for-next

On Mon, Nov 30, 2020 at 12:05 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> Move the function to misc.c
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cache.c     | 24 ------------------------
>  fs/cifs/cifsproto.h |  1 +
>  fs/cifs/fscache.c   |  1 +
>  fs/cifs/fscache.h   |  1 -
>  fs/cifs/misc.c      | 24 ++++++++++++++++++++++++
>  5 files changed, 26 insertions(+), 25 deletions(-)
>
> diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
> index 0f2adecb94f2..488fe0ffc1ef 100644
> --- a/fs/cifs/cache.c
> +++ b/fs/cifs/cache.c
> @@ -53,30 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = {
>         .type = FSCACHE_COOKIE_TYPE_INDEX,
>  };
>
> -char *extract_sharename(const char *treename)
> -{
> -       const char *src;
> -       char *delim, *dst;
> -       int len;
> -
> -       /* skip double chars at the beginning */
> -       src = treename + 2;
> -
> -       /* share name is always preceded by '\\' now */
> -       delim = strchr(src, '\\');
> -       if (!delim)
> -               return ERR_PTR(-EINVAL);
> -       delim++;
> -       len = strlen(delim);
> -
> -       /* caller has to free the memory */
> -       dst = kstrndup(delim, len, GFP_KERNEL);
> -       if (!dst)
> -               return ERR_PTR(-ENOMEM);
> -
> -       return dst;
> -}
> -
>  static enum
>  fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
>                                               const void *data,
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index d716e81d86fa..5f997a01fb45 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -621,6 +621,7 @@ struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
>  void cifs_put_tcp_super(struct super_block *sb);
>  int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
>  char *extract_hostname(const char *unc);
> +char *extract_sharename(const char *unc);
>
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>  static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
> index da688185403c..20d24af33ee2 100644
> --- a/fs/cifs/fscache.c
> +++ b/fs/cifs/fscache.c
> @@ -22,6 +22,7 @@
>  #include "cifsglob.h"
>  #include "cifs_debug.h"
>  #include "cifs_fs_sb.h"
> +#include "cifsproto.h"
>
>  /*
>   * Key layout of CIFS server cache index object
> diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
> index 1091633d2adb..e811f2dd7619 100644
> --- a/fs/cifs/fscache.h
> +++ b/fs/cifs/fscache.h
> @@ -57,7 +57,6 @@ extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
>
>  extern int cifs_fscache_register(void);
>  extern void cifs_fscache_unregister(void);
> -extern char *extract_sharename(const char *);
>
>  /*
>   * fscache.c
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 3d5cc25c167f..f0a1c24751b2 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -1227,3 +1227,27 @@ char *extract_hostname(const char *unc)
>
>         return dst;
>  }
> +
> +char *extract_sharename(const char *unc)
> +{
> +       const char *src;
> +       char *delim, *dst;
> +       int len;
> +
> +       /* skip double chars at the beginning */
> +       src = unc + 2;
> +
> +       /* share name is always preceded by '\\' now */
> +       delim = strchr(src, '\\');
> +       if (!delim)
> +               return ERR_PTR(-EINVAL);
> +       delim++;
> +       len = strlen(delim);
> +
> +       /* caller has to free the memory */
> +       dst = kstrndup(delim, len, GFP_KERNEL);
> +       if (!dst)
> +               return ERR_PTR(-ENOMEM);
> +
> +       return dst;
> +}
> --
> 2.29.2
>


-- 
Thanks,

Steve

[-- Attachment #2: 0002-cifs-Make-extract_sharename-function-public.patch --]
[-- Type: text/x-patch, Size: 3351 bytes --]

From 749765e260720d332eb8f03c3be28557b8ceb954 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:48 +0100
Subject: [PATCH 2/8] cifs: Make extract_sharename function public

Move the function to misc.c

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cache.c     | 24 ------------------------
 fs/cifs/cifsproto.h |  1 +
 fs/cifs/fscache.c   |  1 +
 fs/cifs/fscache.h   |  1 -
 fs/cifs/misc.c      | 24 ++++++++++++++++++++++++
 5 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
index 0f2adecb94f2..488fe0ffc1ef 100644
--- a/fs/cifs/cache.c
+++ b/fs/cifs/cache.c
@@ -53,30 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = {
 	.type = FSCACHE_COOKIE_TYPE_INDEX,
 };
 
-char *extract_sharename(const char *treename)
-{
-	const char *src;
-	char *delim, *dst;
-	int len;
-
-	/* skip double chars at the beginning */
-	src = treename + 2;
-
-	/* share name is always preceded by '\\' now */
-	delim = strchr(src, '\\');
-	if (!delim)
-		return ERR_PTR(-EINVAL);
-	delim++;
-	len = strlen(delim);
-
-	/* caller has to free the memory */
-	dst = kstrndup(delim, len, GFP_KERNEL);
-	if (!dst)
-		return ERR_PTR(-ENOMEM);
-
-	return dst;
-}
-
 static enum
 fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
 					      const void *data,
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 3fe0c4a0d36d..b80b57a66804 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -618,6 +618,7 @@ struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
 int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
 char *extract_hostname(const char *unc);
+char *extract_sharename(const char *unc);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index da688185403c..20d24af33ee2 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -22,6 +22,7 @@
 #include "cifsglob.h"
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
+#include "cifsproto.h"
 
 /*
  * Key layout of CIFS server cache index object
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
index 1091633d2adb..e811f2dd7619 100644
--- a/fs/cifs/fscache.h
+++ b/fs/cifs/fscache.h
@@ -57,7 +57,6 @@ extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
 
 extern int cifs_fscache_register(void);
 extern void cifs_fscache_unregister(void);
-extern char *extract_sharename(const char *);
 
 /*
  * fscache.c
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 3d5cc25c167f..f0a1c24751b2 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1227,3 +1227,27 @@ char *extract_hostname(const char *unc)
 
 	return dst;
 }
+
+char *extract_sharename(const char *unc)
+{
+	const char *src;
+	char *delim, *dst;
+	int len;
+
+	/* skip double chars at the beginning */
+	src = unc + 2;
+
+	/* share name is always preceded by '\\' now */
+	delim = strchr(src, '\\');
+	if (!delim)
+		return ERR_PTR(-EINVAL);
+	delim++;
+	len = strlen(delim);
+
+	/* caller has to free the memory */
+	dst = kstrndup(delim, len, GFP_KERNEL);
+	if (!dst)
+		return ERR_PTR(-ENOMEM);
+
+	return dst;
+}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 03/11] cifs: Register generic netlink family
  2020-11-30 18:02 ` [PATCH v4 03/11] cifs: Register generic netlink family Samuel Cabrero
@ 2020-12-12  5:51   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:51 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

[-- Attachment #1: Type: text/plain, Size: 7490 bytes --]

updated version of the patch attached (rebased on current for-next).
Tentatively merged into cifs-2.6.git for-next

On Mon, Nov 30, 2020 at 12:05 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> Register a new generic netlink family to talk to the witness service
> userspace daemon.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/Kconfig                        | 11 ++++
>  fs/cifs/Makefile                       |  2 +
>  fs/cifs/cifsfs.c                       | 17 ++++++-
>  fs/cifs/netlink.c                      | 69 ++++++++++++++++++++++++++
>  fs/cifs/netlink.h                      | 16 ++++++
>  include/uapi/linux/cifs/cifs_netlink.h | 31 ++++++++++++
>  6 files changed, 145 insertions(+), 1 deletion(-)
>  create mode 100644 fs/cifs/netlink.c
>  create mode 100644 fs/cifs/netlink.h
>  create mode 100644 include/uapi/linux/cifs/cifs_netlink.h
>
> diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
> index 604f65f4b6c5..664ac5c63d39 100644
> --- a/fs/cifs/Kconfig
> +++ b/fs/cifs/Kconfig
> @@ -190,6 +190,17 @@ config CIFS_DFS_UPCALL
>           servers if their addresses change or for implicit mounts of
>           DFS junction points. If unsure, say Y.
>
> +config CIFS_SWN_UPCALL
> +       bool "SWN feature support"
> +       depends on CIFS
> +       help
> +         The Service Witness Protocol (SWN) is used to get notifications
> +         from a highly available server of resource state changes. This
> +         feature enables an upcall mechanism for CIFS which contacts an
> +         userspace daemon to establish the DCE/RPC connection to retrieve
> +         the cluster available interfaces and resource change notifications.
> +         If unsure, say Y.
> +
>  config CIFS_NFSD_EXPORT
>         bool "Allow nfsd to export CIFS file system"
>         depends on CIFS && BROKEN
> diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
> index cd17d0e50f2a..b88fd46ac597 100644
> --- a/fs/cifs/Makefile
> +++ b/fs/cifs/Makefile
> @@ -18,6 +18,8 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
>
>  cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
>
> +cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
> +
>  cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
>
>  cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 472cb7777e3e..8111d0109a2e 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -55,6 +55,9 @@
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>  #include "dfs_cache.h"
>  #endif
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +#include "netlink.h"
> +#endif
>
>  /*
>   * DOS dates from 1980/1/1 through 2107/12/31
> @@ -1617,10 +1620,15 @@ init_cifs(void)
>         if (rc)
>                 goto out_destroy_dfs_cache;
>  #endif /* CONFIG_CIFS_UPCALL */
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       rc = cifs_genl_init();
> +       if (rc)
> +               goto out_register_key_type;
> +#endif /* CONFIG_CIFS_SWN_UPCALL */
>
>         rc = init_cifs_idmap();
>         if (rc)
> -               goto out_register_key_type;
> +               goto out_cifs_swn_init;
>
>         rc = register_filesystem(&cifs_fs_type);
>         if (rc)
> @@ -1636,7 +1644,11 @@ init_cifs(void)
>
>  out_init_cifs_idmap:
>         exit_cifs_idmap();
> +out_cifs_swn_init:
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       cifs_genl_exit();
>  out_register_key_type:
> +#endif
>  #ifdef CONFIG_CIFS_UPCALL
>         exit_cifs_spnego();
>  out_destroy_dfs_cache:
> @@ -1673,6 +1685,9 @@ exit_cifs(void)
>         unregister_filesystem(&smb3_fs_type);
>         cifs_dfs_release_automount_timer();
>         exit_cifs_idmap();
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       cifs_genl_exit();
> +#endif
>  #ifdef CONFIG_CIFS_UPCALL
>         exit_cifs_spnego();
>  #endif
> diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
> new file mode 100644
> index 000000000000..b9154661fa85
> --- /dev/null
> +++ b/fs/cifs/netlink.c
> @@ -0,0 +1,69 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Netlink routines for CIFS
> + *
> + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
> + */
> +
> +#include <net/genetlink.h>
> +#include <uapi/linux/cifs/cifs_netlink.h>
> +
> +#include "netlink.h"
> +#include "cifsglob.h"
> +#include "cifs_debug.h"
> +
> +static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
> +};
> +
> +static struct genl_ops cifs_genl_ops[] = {
> +};
> +
> +static const struct genl_multicast_group cifs_genl_mcgrps[] = {
> +       [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
> +};
> +
> +struct genl_family cifs_genl_family = {
> +       .name           = CIFS_GENL_NAME,
> +       .version        = CIFS_GENL_VERSION,
> +       .hdrsize        = 0,
> +       .maxattr        = CIFS_GENL_ATTR_MAX,
> +       .module         = THIS_MODULE,
> +       .policy         = cifs_genl_policy,
> +       .ops            = cifs_genl_ops,
> +       .n_ops          = ARRAY_SIZE(cifs_genl_ops),
> +       .mcgrps         = cifs_genl_mcgrps,
> +       .n_mcgrps       = ARRAY_SIZE(cifs_genl_mcgrps),
> +};
> +
> +/**
> + * cifs_genl_init - Register generic netlink family
> + *
> + * Return zero if initialized successfully, otherwise non-zero.
> + */
> +int cifs_genl_init(void)
> +{
> +       int ret;
> +
> +       ret = genl_register_family(&cifs_genl_family);
> +       if (ret < 0) {
> +               cifs_dbg(VFS, "%s: failed to register netlink family\n",
> +                               __func__);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cifs_genl_exit - Unregister generic netlink family
> + */
> +void cifs_genl_exit(void)
> +{
> +       int ret;
> +
> +       ret = genl_unregister_family(&cifs_genl_family);
> +       if (ret < 0) {
> +               cifs_dbg(VFS, "%s: failed to unregister netlink family\n",
> +                               __func__);
> +       }
> +}
> diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h
> new file mode 100644
> index 000000000000..e2fa8ed24c54
> --- /dev/null
> +++ b/fs/cifs/netlink.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Netlink routines for CIFS
> + *
> + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
> + */
> +
> +#ifndef _CIFS_NETLINK_H
> +#define _CIFS_NETLINK_H
> +
> +extern struct genl_family cifs_genl_family;
> +
> +extern int cifs_genl_init(void);
> +extern void cifs_genl_exit(void);
> +
> +#endif /* _CIFS_NETLINK_H */
> diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
> new file mode 100644
> index 000000000000..cdb1bd78fbc7
> --- /dev/null
> +++ b/include/uapi/linux/cifs/cifs_netlink.h
> @@ -0,0 +1,31 @@
> +/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */
> +/*
> + * Netlink routines for CIFS
> + *
> + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
> + */
> +
> +
> +#ifndef _UAPILINUX_CIFS_NETLINK_H
> +#define _UAPILINUX_CIFS_NETLINK_H
> +
> +#define CIFS_GENL_NAME                 "cifs"
> +#define CIFS_GENL_VERSION              0x1
> +
> +#define CIFS_GENL_MCGRP_SWN_NAME       "cifs_mcgrp_swn"
> +
> +enum cifs_genl_multicast_groups {
> +       CIFS_GENL_MCGRP_SWN,
> +};
> +
> +enum cifs_genl_attributes {
> +       __CIFS_GENL_ATTR_MAX,
> +};
> +#define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
> +
> +enum cifs_genl_commands {
> +       __CIFS_GENL_CMD_MAX
> +};
> +#define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
> +
> +#endif /* _UAPILINUX_CIFS_NETLINK_H */
> --
> 2.29.2
>


-- 
Thanks,

Steve

[-- Attachment #2: 0005-cifs-Register-generic-netlink-family.patch --]
[-- Type: text/x-patch, Size: 6444 bytes --]

From cf6a6c0841bc125a9ede50000c22f0110eea805b Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:49 +0100
Subject: [PATCH 5/8] cifs: Register generic netlink family

Register a new generic netlink family to talk to the witness service
userspace daemon.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
---
 fs/cifs/Kconfig                        | 11 ++++
 fs/cifs/Makefile                       |  2 +
 fs/cifs/cifsfs.c                       | 17 ++++++-
 fs/cifs/netlink.c                      | 69 ++++++++++++++++++++++++++
 fs/cifs/netlink.h                      | 16 ++++++
 include/uapi/linux/cifs/cifs_netlink.h | 31 ++++++++++++
 6 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 fs/cifs/netlink.c
 create mode 100644 fs/cifs/netlink.h
 create mode 100644 include/uapi/linux/cifs/cifs_netlink.h

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 604f65f4b6c5..664ac5c63d39 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -190,6 +190,17 @@ config CIFS_DFS_UPCALL
 	  servers if their addresses change or for implicit mounts of
 	  DFS junction points. If unsure, say Y.
 
+config CIFS_SWN_UPCALL
+	bool "SWN feature support"
+	depends on CIFS
+	help
+	  The Service Witness Protocol (SWN) is used to get notifications
+	  from a highly available server of resource state changes. This
+	  feature enables an upcall mechanism for CIFS which contacts an
+	  userspace daemon to establish the DCE/RPC connection to retrieve
+	  the cluster available interfaces and resource change notifications.
+	  If unsure, say Y.
+
 config CIFS_NFSD_EXPORT
 	bool "Allow nfsd to export CIFS file system"
 	depends on CIFS && BROKEN
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 848ebad6af7d..9e398d227b0e 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -18,6 +18,8 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
 
+cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
+
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
 
 cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 4f27f77d3053..5d32561ae2ed 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -55,6 +55,9 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "netlink.h"
+#endif
 #include "fs_context.h"
 
 /*
@@ -1601,10 +1604,15 @@ init_cifs(void)
 	if (rc)
 		goto out_destroy_dfs_cache;
 #endif /* CONFIG_CIFS_UPCALL */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	rc = cifs_genl_init();
+	if (rc)
+		goto out_register_key_type;
+#endif /* CONFIG_CIFS_SWN_UPCALL */
 
 	rc = init_cifs_idmap();
 	if (rc)
-		goto out_register_key_type;
+		goto out_cifs_swn_init;
 
 	rc = register_filesystem(&cifs_fs_type);
 	if (rc)
@@ -1620,7 +1628,11 @@ init_cifs(void)
 
 out_init_cifs_idmap:
 	exit_cifs_idmap();
+out_cifs_swn_init:
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
 out_register_key_type:
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 out_destroy_dfs_cache:
@@ -1657,6 +1669,9 @@ exit_cifs(void)
 	unregister_filesystem(&smb3_fs_type);
 	cifs_dfs_release_automount_timer();
 	exit_cifs_idmap();
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	cifs_genl_exit();
+#endif
 #ifdef CONFIG_CIFS_UPCALL
 	exit_cifs_spnego();
 #endif
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
new file mode 100644
index 000000000000..b9154661fa85
--- /dev/null
+++ b/fs/cifs/netlink.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#include <net/genetlink.h>
+#include <uapi/linux/cifs/cifs_netlink.h>
+
+#include "netlink.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+
+static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
+};
+
+static struct genl_ops cifs_genl_ops[] = {
+};
+
+static const struct genl_multicast_group cifs_genl_mcgrps[] = {
+	[CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
+};
+
+struct genl_family cifs_genl_family = {
+	.name		= CIFS_GENL_NAME,
+	.version	= CIFS_GENL_VERSION,
+	.hdrsize	= 0,
+	.maxattr	= CIFS_GENL_ATTR_MAX,
+	.module		= THIS_MODULE,
+	.policy		= cifs_genl_policy,
+	.ops		= cifs_genl_ops,
+	.n_ops		= ARRAY_SIZE(cifs_genl_ops),
+	.mcgrps		= cifs_genl_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(cifs_genl_mcgrps),
+};
+
+/**
+ * cifs_genl_init - Register generic netlink family
+ *
+ * Return zero if initialized successfully, otherwise non-zero.
+ */
+int cifs_genl_init(void)
+{
+	int ret;
+
+	ret = genl_register_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to register netlink family\n",
+				__func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * cifs_genl_exit - Unregister generic netlink family
+ */
+void cifs_genl_exit(void)
+{
+	int ret;
+
+	ret = genl_unregister_family(&cifs_genl_family);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: failed to unregister netlink family\n",
+				__func__);
+	}
+}
diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h
new file mode 100644
index 000000000000..e2fa8ed24c54
--- /dev/null
+++ b/fs/cifs/netlink.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#ifndef _CIFS_NETLINK_H
+#define _CIFS_NETLINK_H
+
+extern struct genl_family cifs_genl_family;
+
+extern int cifs_genl_init(void);
+extern void cifs_genl_exit(void);
+
+#endif /* _CIFS_NETLINK_H */
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
new file mode 100644
index 000000000000..cdb1bd78fbc7
--- /dev/null
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */
+/*
+ * Netlink routines for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+
+#ifndef _UAPILINUX_CIFS_NETLINK_H
+#define _UAPILINUX_CIFS_NETLINK_H
+
+#define CIFS_GENL_NAME			"cifs"
+#define CIFS_GENL_VERSION		0x1
+
+#define CIFS_GENL_MCGRP_SWN_NAME	"cifs_mcgrp_swn"
+
+enum cifs_genl_multicast_groups {
+	CIFS_GENL_MCGRP_SWN,
+};
+
+enum cifs_genl_attributes {
+	__CIFS_GENL_ATTR_MAX,
+};
+#define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
+
+enum cifs_genl_commands {
+	__CIFS_GENL_CMD_MAX
+};
+#define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
+
+#endif /* _UAPILINUX_CIFS_NETLINK_H */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon
  2020-11-30 18:02 ` [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon Samuel Cabrero
@ 2020-12-12  5:52   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:52 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS, Aurélien Aptel

[-- Attachment #1: Type: text/plain, Size: 20765 bytes --]

updated version of the patch attached (rebased on current for-next).
Tentatively merged into cifs-2.6.git for-next


On Mon, Nov 30, 2020 at 12:06 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> + Define the generic netlink family commands and message attributes to
>   communicate with the userspace daemon
>
> + The register and unregister commands are sent when connecting or
>   disconnecting a tree. The witness registration keeps a pointer to
>   the tcon and has the same lifetime.
>
> + Each registration has an id allocated by an IDR. This id is sent to the
>   userspace daemon in the register command, and will be included in the
>   notification messages from the userspace daemon to retrieve from the
>   IDR the matching registration.
>
> + The authentication information is bundled in the register message.
>   If kerberos is used the message just carries a flag.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/Makefile                       |   2 +-
>  fs/cifs/cifs_swn.c                     | 421 +++++++++++++++++++++++++
>  fs/cifs/cifs_swn.h                     |  17 +
>  fs/cifs/connect.c                      |  26 +-
>  fs/cifs/netlink.c                      |  11 +
>  include/uapi/linux/cifs/cifs_netlink.h |  15 +
>  6 files changed, 489 insertions(+), 3 deletions(-)
>  create mode 100644 fs/cifs/cifs_swn.c
>  create mode 100644 fs/cifs/cifs_swn.h
>
> diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
> index b88fd46ac597..abb2fbc0f904 100644
> --- a/fs/cifs/Makefile
> +++ b/fs/cifs/Makefile
> @@ -18,7 +18,7 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
>
>  cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
>
> -cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
> +cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
>
>  cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
>
> diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
> new file mode 100644
> index 000000000000..c0af03955d0c
> --- /dev/null
> +++ b/fs/cifs/cifs_swn.c
> @@ -0,0 +1,421 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Witness Service client for CIFS
> + *
> + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
> + */
> +
> +#include <linux/kref.h>
> +#include <net/genetlink.h>
> +#include <uapi/linux/cifs/cifs_netlink.h>
> +
> +#include "cifs_swn.h"
> +#include "cifsglob.h"
> +#include "cifsproto.h"
> +#include "fscache.h"
> +#include "cifs_debug.h"
> +#include "netlink.h"
> +
> +static DEFINE_IDR(cifs_swnreg_idr);
> +static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
> +
> +struct cifs_swn_reg {
> +       int id;
> +       struct kref ref_count;
> +
> +       const char *net_name;
> +       const char *share_name;
> +       bool net_name_notify;
> +       bool share_name_notify;
> +       bool ip_notify;
> +
> +       struct cifs_tcon *tcon;
> +};
> +
> +static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
> +{
> +       int ret;
> +
> +       ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH);
> +       if (ret < 0)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
> +{
> +       int ret;
> +
> +       if (tcon->ses->user_name != NULL) {
> +               ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       if (tcon->ses->password != NULL) {
> +               ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       if (tcon->ses->domainName != NULL) {
> +               ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * Sends a register message to the userspace daemon based on the registration.
> + * The authentication information to connect to the witness service is bundled
> + * into the message.
> + */
> +static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
> +{
> +       struct sk_buff *skb;
> +       struct genlmsghdr *hdr;
> +       enum securityEnum authtype;
> +       int ret;
> +
> +       skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> +       if (skb == NULL) {
> +               ret = -ENOMEM;
> +               goto fail;
> +       }
> +
> +       hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
> +       if (hdr == NULL) {
> +               ret = -ENOMEM;
> +               goto nlmsg_fail;
> +       }
> +
> +       ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
> +                       &swnreg->tcon->ses->server->dstaddr);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       if (swnreg->net_name_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       if (swnreg->share_name_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       if (swnreg->ip_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
> +       switch (authtype) {
> +       case Kerberos:
> +               ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
> +               if (ret < 0) {
> +                       cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
> +                       goto nlmsg_fail;
> +               }
> +               break;
> +       case LANMAN:
> +       case NTLM:
> +       case NTLMv2:
> +       case RawNTLMSSP:
> +               ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
> +               if (ret < 0) {
> +                       cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
> +                       goto nlmsg_fail;
> +               }
> +               break;
> +       default:
> +               cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
> +               ret = -EINVAL;
> +               goto nlmsg_fail;
> +       }
> +
> +       genlmsg_end(skb, hdr);
> +       genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
> +
> +       cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
> +                       swnreg->net_name, swnreg->id);
> +
> +       return 0;
> +
> +nlmsg_fail:
> +       genlmsg_cancel(skb, hdr);
> +       nlmsg_free(skb);
> +fail:
> +       return ret;
> +}
> +
> +/*
> + * Sends an uregister message to the userspace daemon based on the registration
> + */
> +static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
> +{
> +       struct sk_buff *skb;
> +       struct genlmsghdr *hdr;
> +       int ret;
> +
> +       skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> +       if (skb == NULL)
> +               return -ENOMEM;
> +
> +       hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
> +       if (hdr == NULL) {
> +               ret = -ENOMEM;
> +               goto nlmsg_fail;
> +       }
> +
> +       ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
> +                       &swnreg->tcon->ses->server->dstaddr);
> +       if (ret < 0)
> +               goto nlmsg_fail;
> +
> +       if (swnreg->net_name_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       if (swnreg->share_name_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       if (swnreg->ip_notify) {
> +               ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
> +               if (ret < 0)
> +                       goto nlmsg_fail;
> +       }
> +
> +       genlmsg_end(skb, hdr);
> +       genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
> +
> +       cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
> +                       swnreg->net_name, swnreg->id);
> +
> +       return 0;
> +
> +nlmsg_fail:
> +       genlmsg_cancel(skb, hdr);
> +       nlmsg_free(skb);
> +       return ret;
> +}
> +
> +/*
> + * Try to find a matching registration for the tcon's server name and share name.
> + * Calls to this funciton must be protected by cifs_swnreg_idr_mutex.
> + * TODO Try to avoid memory allocations
> + */
> +static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
> +{
> +       struct cifs_swn_reg *swnreg;
> +       int id;
> +       const char *share_name;
> +       const char *net_name;
> +
> +       net_name = extract_hostname(tcon->treeName);
> +       if (IS_ERR_OR_NULL(net_name)) {
> +               int ret;
> +
> +               ret = PTR_ERR(net_name);
> +               cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
> +                               __func__, tcon->treeName, ret);
> +               return NULL;
> +       }
> +
> +       share_name = extract_sharename(tcon->treeName);
> +       if (IS_ERR_OR_NULL(share_name)) {
> +               int ret;
> +
> +               ret = PTR_ERR(net_name);
> +               cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
> +                               __func__, tcon->treeName, ret);
> +               kfree(net_name);
> +               return NULL;
> +       }
> +
> +       idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
> +               if (strcasecmp(swnreg->net_name, net_name) != 0
> +                   || strcasecmp(swnreg->share_name, share_name) != 0) {
> +                       continue;
> +               }
> +
> +               mutex_unlock(&cifs_swnreg_idr_mutex);
> +
> +               cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
> +                               swnreg->share_name);
> +
> +               kfree(net_name);
> +               kfree(share_name);
> +
> +               return swnreg;
> +       }
> +
> +       kfree(net_name);
> +       kfree(share_name);
> +
> +       return NULL;
> +}
> +
> +/*
> + * Get a registration for the tcon's server and share name, allocating a new one if it does not
> + * exists
> + */
> +static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
> +{
> +       struct cifs_swn_reg *reg = NULL;
> +       int ret;
> +
> +       mutex_lock(&cifs_swnreg_idr_mutex);
> +
> +       /* Check if we are already registered for this network and share names */
> +       reg = cifs_find_swn_reg(tcon);
> +       if (IS_ERR(reg)) {
> +               return reg;
> +       } else if (reg != NULL) {
> +               kref_get(&reg->ref_count);
> +               mutex_unlock(&cifs_swnreg_idr_mutex);
> +               return reg;
> +       }
> +
> +       reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
> +       if (reg == NULL) {
> +               mutex_unlock(&cifs_swnreg_idr_mutex);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       kref_init(&reg->ref_count);
> +
> +       reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
> +       if (reg->id < 0) {
> +               cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
> +               ret = reg->id;
> +               goto fail;
> +       }
> +
> +       reg->net_name = extract_hostname(tcon->treeName);
> +       if (IS_ERR(reg->net_name)) {
> +               ret = PTR_ERR(reg->net_name);
> +               cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
> +               goto fail_idr;
> +       }
> +
> +       reg->share_name = extract_sharename(tcon->treeName);
> +       if (IS_ERR(reg->share_name)) {
> +               ret = PTR_ERR(reg->share_name);
> +               cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
> +               goto fail_net_name;
> +       }
> +
> +       reg->net_name_notify = true;
> +       reg->share_name_notify = true;
> +       reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
> +
> +       reg->tcon = tcon;
> +
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +
> +       return reg;
> +
> +fail_net_name:
> +       kfree(reg->net_name);
> +fail_idr:
> +       idr_remove(&cifs_swnreg_idr, reg->id);
> +fail:
> +       kfree(reg);
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +       return ERR_PTR(ret);
> +}
> +
> +static void cifs_swn_reg_release(struct kref *ref)
> +{
> +       struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
> +       int ret;
> +
> +       ret = cifs_swn_send_unregister_message(swnreg);
> +       if (ret < 0)
> +               cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
> +
> +       idr_remove(&cifs_swnreg_idr, swnreg->id);
> +       kfree(swnreg->net_name);
> +       kfree(swnreg->share_name);
> +       kfree(swnreg);
> +}
> +
> +static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
> +{
> +       mutex_lock(&cifs_swnreg_idr_mutex);
> +       kref_put(&swnreg->ref_count, cifs_swn_reg_release);
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +}
> +
> +int cifs_swn_register(struct cifs_tcon *tcon)
> +{
> +       struct cifs_swn_reg *swnreg;
> +       int ret;
> +
> +       swnreg = cifs_get_swn_reg(tcon);
> +       if (IS_ERR(swnreg))
> +               return PTR_ERR(swnreg);
> +
> +       ret = cifs_swn_send_register_message(swnreg);
> +       if (ret < 0) {
> +               cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
> +               /* Do not put the swnreg or return error, the echo task will retry */
> +       }
> +
> +       return 0;
> +}
> +
> +int cifs_swn_unregister(struct cifs_tcon *tcon)
> +{
> +       struct cifs_swn_reg *swnreg;
> +
> +       mutex_lock(&cifs_swnreg_idr_mutex);
> +
> +       swnreg = cifs_find_swn_reg(tcon);
> +       if (swnreg == NULL) {
> +               mutex_unlock(&cifs_swnreg_idr_mutex);
> +               return -EEXIST;
> +       }
> +
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +
> +       cifs_put_swn_reg(swnreg);
> +
> +       return 0;
> +}
> diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
> new file mode 100644
> index 000000000000..69c7bd1035da
> --- /dev/null
> +++ b/fs/cifs/cifs_swn.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Witness Service client for CIFS
> + *
> + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
> + */
> +
> +#ifndef _CIFS_SWN_H
> +#define _CIFS_SWN_H
> +
> +struct cifs_tcon;
> +
> +extern int cifs_swn_register(struct cifs_tcon *tcon);
> +
> +extern int cifs_swn_unregister(struct cifs_tcon *tcon);
> +
> +#endif /* _CIFS_SWN_H */
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 22d46c8acc7f..7fbb201b42c3 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -62,6 +62,9 @@
>  #include "dfs_cache.h"
>  #endif
>  #include "fs_context.h"
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +#include "cifs_swn.h"
> +#endif
>
>  extern mempool_t *cifs_req_poolp;
>  extern bool disable_legacy_dialects;
> @@ -3168,7 +3171,17 @@ cifs_put_tcon(struct cifs_tcon *tcon)
>                 return;
>         }
>
> -       /* TODO witness unregister */
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       if (tcon->use_witness) {
> +               int rc;
> +
> +               rc = cifs_swn_unregister(tcon);
> +               if (rc < 0) {
> +                       cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
> +                                       __func__, rc);
> +               }
> +       }
> +#endif
>
>         list_del_init(&tcon->tcon_list);
>         spin_unlock(&cifs_tcp_ses_lock);
> @@ -3336,8 +3349,17 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
>         if (volume_info->witness) {
>                 if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
>                         if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
> -                               /* TODO witness register */
> +                               /*
> +                                * Set witness in use flag in first place
> +                                * to retry registration in the echo task
> +                                */
>                                 tcon->use_witness = true;
> +                               /* And try to register immediately */
> +                               rc = cifs_swn_register(tcon);
> +                               if (rc < 0) {
> +                                       cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc);
> +                                       goto out_fail;
> +                               }
>                         } else {
>                                 cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
>                                 rc = -EOPNOTSUPP;
> diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
> index b9154661fa85..83008a56def5 100644
> --- a/fs/cifs/netlink.c
> +++ b/fs/cifs/netlink.c
> @@ -13,6 +13,17 @@
>  #include "cifs_debug.h"
>
>  static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
> +       [CIFS_GENL_ATTR_SWN_REGISTRATION_ID]    = { .type = NLA_U32 },
> +       [CIFS_GENL_ATTR_SWN_NET_NAME]           = { .type = NLA_STRING },
> +       [CIFS_GENL_ATTR_SWN_SHARE_NAME]         = { .type = NLA_STRING },
> +       [CIFS_GENL_ATTR_SWN_IP]                 = { .len = sizeof(struct sockaddr_storage) },
> +       [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY]    = { .type = NLA_FLAG },
> +       [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY]  = { .type = NLA_FLAG },
> +       [CIFS_GENL_ATTR_SWN_IP_NOTIFY]          = { .type = NLA_FLAG },
> +       [CIFS_GENL_ATTR_SWN_KRB_AUTH]           = { .type = NLA_FLAG },
> +       [CIFS_GENL_ATTR_SWN_USER_NAME]          = { .type = NLA_STRING },
> +       [CIFS_GENL_ATTR_SWN_PASSWORD]           = { .type = NLA_STRING },
> +       [CIFS_GENL_ATTR_SWN_DOMAIN_NAME]        = { .type = NLA_STRING },
>  };
>
>  static struct genl_ops cifs_genl_ops[] = {
> diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
> index cdb1bd78fbc7..5662e2774513 100644
> --- a/include/uapi/linux/cifs/cifs_netlink.h
> +++ b/include/uapi/linux/cifs/cifs_netlink.h
> @@ -19,11 +19,26 @@ enum cifs_genl_multicast_groups {
>  };
>
>  enum cifs_genl_attributes {
> +       CIFS_GENL_ATTR_UNSPEC,
> +       CIFS_GENL_ATTR_SWN_REGISTRATION_ID,
> +       CIFS_GENL_ATTR_SWN_NET_NAME,
> +       CIFS_GENL_ATTR_SWN_SHARE_NAME,
> +       CIFS_GENL_ATTR_SWN_IP,
> +       CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY,
> +       CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY,
> +       CIFS_GENL_ATTR_SWN_IP_NOTIFY,
> +       CIFS_GENL_ATTR_SWN_KRB_AUTH,
> +       CIFS_GENL_ATTR_SWN_USER_NAME,
> +       CIFS_GENL_ATTR_SWN_PASSWORD,
> +       CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
>         __CIFS_GENL_ATTR_MAX,
>  };
>  #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
>
>  enum cifs_genl_commands {
> +       CIFS_GENL_CMD_UNSPEC,
> +       CIFS_GENL_CMD_SWN_REGISTER,
> +       CIFS_GENL_CMD_SWN_UNREGISTER,
>         __CIFS_GENL_CMD_MAX
>  };
>  #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
> --
> 2.29.2
>


-- 
Thanks,

Steve

[-- Attachment #2: 0008-cifs-Send-witness-register-and-unregister-commands-t.patch --]
[-- Type: text/x-patch, Size: 16370 bytes --]

From d592a0054e8741d60324d1ec067375f5479f3295 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Mon, 30 Nov 2020 19:02:51 +0100
Subject: [PATCH 8/8] cifs: Send witness register and unregister commands to
 userspace daemon

+ Define the generic netlink family commands and message attributes to
  communicate with the userspace daemon

+ The register and unregister commands are sent when connecting or
  disconnecting a tree. The witness registration keeps a pointer to
  the tcon and has the same lifetime.

+ Each registration has an id allocated by an IDR. This id is sent to the
  userspace daemon in the register command, and will be included in the
  notification messages from the userspace daemon to retrieve from the
  IDR the matching registration.

+ The authentication information is bundled in the register message.
  If kerberos is used the message just carries a flag.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/Makefile                       |   2 +-
 fs/cifs/cifs_swn.c                     | 421 +++++++++++++++++++++++++
 fs/cifs/cifs_swn.h                     |  17 +
 fs/cifs/connect.c                      |  26 +-
 fs/cifs/netlink.c                      |  11 +
 include/uapi/linux/cifs/cifs_netlink.h |  15 +
 6 files changed, 489 insertions(+), 3 deletions(-)
 create mode 100644 fs/cifs/cifs_swn.c
 create mode 100644 fs/cifs/cifs_swn.h

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 9e398d227b0e..5213b20843b5 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -18,7 +18,7 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
 
-cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o
+cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
 
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
 
diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
new file mode 100644
index 000000000000..c0af03955d0c
--- /dev/null
+++ b/fs/cifs/cifs_swn.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Witness Service client for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#include <linux/kref.h>
+#include <net/genetlink.h>
+#include <uapi/linux/cifs/cifs_netlink.h>
+
+#include "cifs_swn.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "fscache.h"
+#include "cifs_debug.h"
+#include "netlink.h"
+
+static DEFINE_IDR(cifs_swnreg_idr);
+static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
+
+struct cifs_swn_reg {
+	int id;
+	struct kref ref_count;
+
+	const char *net_name;
+	const char *share_name;
+	bool net_name_notify;
+	bool share_name_notify;
+	bool ip_notify;
+
+	struct cifs_tcon *tcon;
+};
+
+static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
+{
+	int ret;
+
+	ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
+{
+	int ret;
+
+	if (tcon->ses->user_name != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (tcon->ses->password != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (tcon->ses->domainName != NULL) {
+		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Sends a register message to the userspace daemon based on the registration.
+ * The authentication information to connect to the witness service is bundled
+ * into the message.
+ */
+static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
+{
+	struct sk_buff *skb;
+	struct genlmsghdr *hdr;
+	enum securityEnum authtype;
+	int ret;
+
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
+	if (hdr == NULL) {
+		ret = -ENOMEM;
+		goto nlmsg_fail;
+	}
+
+	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+			&swnreg->tcon->ses->server->dstaddr);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	if (swnreg->net_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->share_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->ip_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
+	switch (authtype) {
+	case Kerberos:
+		ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
+			goto nlmsg_fail;
+		}
+		break;
+	case LANMAN:
+	case NTLM:
+	case NTLMv2:
+	case RawNTLMSSP:
+		ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
+		if (ret < 0) {
+			cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
+			goto nlmsg_fail;
+		}
+		break;
+	default:
+		cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
+		ret = -EINVAL;
+		goto nlmsg_fail;
+	}
+
+	genlmsg_end(skb, hdr);
+	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
+
+	cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
+			swnreg->net_name, swnreg->id);
+
+	return 0;
+
+nlmsg_fail:
+	genlmsg_cancel(skb, hdr);
+	nlmsg_free(skb);
+fail:
+	return ret;
+}
+
+/*
+ * Sends an uregister message to the userspace daemon based on the registration
+ */
+static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
+{
+	struct sk_buff *skb;
+	struct genlmsghdr *hdr;
+	int ret;
+
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
+	if (hdr == NULL) {
+		ret = -ENOMEM;
+		goto nlmsg_fail;
+	}
+
+	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+			&swnreg->tcon->ses->server->dstaddr);
+	if (ret < 0)
+		goto nlmsg_fail;
+
+	if (swnreg->net_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->share_name_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	if (swnreg->ip_notify) {
+		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
+		if (ret < 0)
+			goto nlmsg_fail;
+	}
+
+	genlmsg_end(skb, hdr);
+	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
+
+	cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
+			swnreg->net_name, swnreg->id);
+
+	return 0;
+
+nlmsg_fail:
+	genlmsg_cancel(skb, hdr);
+	nlmsg_free(skb);
+	return ret;
+}
+
+/*
+ * Try to find a matching registration for the tcon's server name and share name.
+ * Calls to this funciton must be protected by cifs_swnreg_idr_mutex.
+ * TODO Try to avoid memory allocations
+ */
+static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+	int id;
+	const char *share_name;
+	const char *net_name;
+
+	net_name = extract_hostname(tcon->treeName);
+	if (IS_ERR_OR_NULL(net_name)) {
+		int ret;
+
+		ret = PTR_ERR(net_name);
+		cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
+				__func__, tcon->treeName, ret);
+		return NULL;
+	}
+
+	share_name = extract_sharename(tcon->treeName);
+	if (IS_ERR_OR_NULL(share_name)) {
+		int ret;
+
+		ret = PTR_ERR(net_name);
+		cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
+				__func__, tcon->treeName, ret);
+		kfree(net_name);
+		return NULL;
+	}
+
+	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+		if (strcasecmp(swnreg->net_name, net_name) != 0
+		    || strcasecmp(swnreg->share_name, share_name) != 0) {
+			continue;
+		}
+
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+
+		cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
+				swnreg->share_name);
+
+		kfree(net_name);
+		kfree(share_name);
+
+		return swnreg;
+	}
+
+	kfree(net_name);
+	kfree(share_name);
+
+	return NULL;
+}
+
+/*
+ * Get a registration for the tcon's server and share name, allocating a new one if it does not
+ * exists
+ */
+static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *reg = NULL;
+	int ret;
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+
+	/* Check if we are already registered for this network and share names */
+	reg = cifs_find_swn_reg(tcon);
+	if (IS_ERR(reg)) {
+		return reg;
+	} else if (reg != NULL) {
+		kref_get(&reg->ref_count);
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return reg;
+	}
+
+	reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
+	if (reg == NULL) {
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	kref_init(&reg->ref_count);
+
+	reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
+	if (reg->id < 0) {
+		cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
+		ret = reg->id;
+		goto fail;
+	}
+
+	reg->net_name = extract_hostname(tcon->treeName);
+	if (IS_ERR(reg->net_name)) {
+		ret = PTR_ERR(reg->net_name);
+		cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
+		goto fail_idr;
+	}
+
+	reg->share_name = extract_sharename(tcon->treeName);
+	if (IS_ERR(reg->share_name)) {
+		ret = PTR_ERR(reg->share_name);
+		cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
+		goto fail_net_name;
+	}
+
+	reg->net_name_notify = true;
+	reg->share_name_notify = true;
+	reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
+
+	reg->tcon = tcon;
+
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+
+	return reg;
+
+fail_net_name:
+	kfree(reg->net_name);
+fail_idr:
+	idr_remove(&cifs_swnreg_idr, reg->id);
+fail:
+	kfree(reg);
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+	return ERR_PTR(ret);
+}
+
+static void cifs_swn_reg_release(struct kref *ref)
+{
+	struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
+	int ret;
+
+	ret = cifs_swn_send_unregister_message(swnreg);
+	if (ret < 0)
+		cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
+
+	idr_remove(&cifs_swnreg_idr, swnreg->id);
+	kfree(swnreg->net_name);
+	kfree(swnreg->share_name);
+	kfree(swnreg);
+}
+
+static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
+{
+	mutex_lock(&cifs_swnreg_idr_mutex);
+	kref_put(&swnreg->ref_count, cifs_swn_reg_release);
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+}
+
+int cifs_swn_register(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+	int ret;
+
+	swnreg = cifs_get_swn_reg(tcon);
+	if (IS_ERR(swnreg))
+		return PTR_ERR(swnreg);
+
+	ret = cifs_swn_send_register_message(swnreg);
+	if (ret < 0) {
+		cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
+		/* Do not put the swnreg or return error, the echo task will retry */
+	}
+
+	return 0;
+}
+
+int cifs_swn_unregister(struct cifs_tcon *tcon)
+{
+	struct cifs_swn_reg *swnreg;
+
+	mutex_lock(&cifs_swnreg_idr_mutex);
+
+	swnreg = cifs_find_swn_reg(tcon);
+	if (swnreg == NULL) {
+		mutex_unlock(&cifs_swnreg_idr_mutex);
+		return -EEXIST;
+	}
+
+	mutex_unlock(&cifs_swnreg_idr_mutex);
+
+	cifs_put_swn_reg(swnreg);
+
+	return 0;
+}
diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
new file mode 100644
index 000000000000..69c7bd1035da
--- /dev/null
+++ b/fs/cifs/cifs_swn.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Witness Service client for CIFS
+ *
+ * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
+ */
+
+#ifndef _CIFS_SWN_H
+#define _CIFS_SWN_H
+
+struct cifs_tcon;
+
+extern int cifs_swn_register(struct cifs_tcon *tcon);
+
+extern int cifs_swn_unregister(struct cifs_tcon *tcon);
+
+#endif /* _CIFS_SWN_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index ead1c086b88d..68ef2da7c74b 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -62,6 +62,9 @@
 #include "dfs_cache.h"
 #endif
 #include "fs_context.h"
+#ifdef CONFIG_CIFS_SWN_UPCALL
+#include "cifs_swn.h"
+#endif
 
 extern mempool_t *cifs_req_poolp;
 extern bool disable_legacy_dialects;
@@ -1944,7 +1947,17 @@ cifs_put_tcon(struct cifs_tcon *tcon)
 		return;
 	}
 
-	/* TODO witness unregister */
+#ifdef CONFIG_CIFS_SWN_UPCALL
+	if (tcon->use_witness) {
+		int rc;
+
+		rc = cifs_swn_unregister(tcon);
+		if (rc < 0) {
+			cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
+					__func__, rc);
+		}
+	}
+#endif
 
 	list_del_init(&tcon->tcon_list);
 	spin_unlock(&cifs_tcp_ses_lock);
@@ -2111,8 +2124,17 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
 	if (ctx->witness) {
 		if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
 			if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
-				/* TODO witness register */
+				/*
+				 * Set witness in use flag in first place
+				 * to retry registration in the echo task
+				 */
 				tcon->use_witness = true;
+				/* And try to register immediately */
+				rc = cifs_swn_register(tcon);
+				if (rc < 0) {
+					cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc);
+					goto out_fail;
+				}
 			} else {
 				/* TODO: try to extend for non-cluster uses (eg multichannel) */
 				cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
index b9154661fa85..83008a56def5 100644
--- a/fs/cifs/netlink.c
+++ b/fs/cifs/netlink.c
@@ -13,6 +13,17 @@
 #include "cifs_debug.h"
 
 static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
+	[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]	= { .type = NLA_U32 },
+	[CIFS_GENL_ATTR_SWN_NET_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_SHARE_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_IP]			= { .len = sizeof(struct sockaddr_storage) },
+	[CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY]	= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY]	= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_IP_NOTIFY]		= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_KRB_AUTH]		= { .type = NLA_FLAG },
+	[CIFS_GENL_ATTR_SWN_USER_NAME]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_PASSWORD]		= { .type = NLA_STRING },
+	[CIFS_GENL_ATTR_SWN_DOMAIN_NAME]	= { .type = NLA_STRING },
 };
 
 static struct genl_ops cifs_genl_ops[] = {
diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
index cdb1bd78fbc7..5662e2774513 100644
--- a/include/uapi/linux/cifs/cifs_netlink.h
+++ b/include/uapi/linux/cifs/cifs_netlink.h
@@ -19,11 +19,26 @@ enum cifs_genl_multicast_groups {
 };
 
 enum cifs_genl_attributes {
+	CIFS_GENL_ATTR_UNSPEC,
+	CIFS_GENL_ATTR_SWN_REGISTRATION_ID,
+	CIFS_GENL_ATTR_SWN_NET_NAME,
+	CIFS_GENL_ATTR_SWN_SHARE_NAME,
+	CIFS_GENL_ATTR_SWN_IP,
+	CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY,
+	CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY,
+	CIFS_GENL_ATTR_SWN_IP_NOTIFY,
+	CIFS_GENL_ATTR_SWN_KRB_AUTH,
+	CIFS_GENL_ATTR_SWN_USER_NAME,
+	CIFS_GENL_ATTR_SWN_PASSWORD,
+	CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
 	__CIFS_GENL_ATTR_MAX,
 };
 #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
 
 enum cifs_genl_commands {
+	CIFS_GENL_CMD_UNSPEC,
+	CIFS_GENL_CMD_SWN_REGISTER,
+	CIFS_GENL_CMD_SWN_UNREGISTER,
 	__CIFS_GENL_CMD_MAX
 };
 #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 06/11] cifs: Set witness notification handler for messages from userspace daemon
  2020-11-30 18:02 ` [PATCH v4 06/11] cifs: Set witness notification handler for messages from " Samuel Cabrero
@ 2020-12-12  5:55   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:55 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

tentatively merged into cifs-2.6.git for-next    Let me know if any
updates needed to it

On Mon, Nov 30, 2020 at 12:05 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> + Set a handler for the witness notification messages received from the
>   userspace daemon.
>
> + Handle the resource state change notification. When the resource
>   becomes unavailable or available set the tcp status to
>   CifsNeedReconnect for all channels.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cifs_swn.c                     | 86 ++++++++++++++++++++++++++
>  fs/cifs/cifs_swn.h                     |  4 ++
>  fs/cifs/netlink.c                      |  9 +++
>  include/uapi/linux/cifs/cifs_netlink.h | 17 +++++
>  4 files changed, 116 insertions(+)
>
> diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
> index c0af03955d0c..63b0764af5d5 100644
> --- a/fs/cifs/cifs_swn.c
> +++ b/fs/cifs/cifs_swn.c
> @@ -383,6 +383,92 @@ static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
>         mutex_unlock(&cifs_swnreg_idr_mutex);
>  }
>
> +static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
> +{
> +       int i;
> +
> +       switch (state) {
> +       case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
> +               cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
> +               for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
> +                       spin_lock(&GlobalMid_Lock);
> +                       if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
> +                               swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
> +                       spin_unlock(&GlobalMid_Lock);
> +               }
> +               break;
> +       case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
> +               cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
> +               for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
> +                       spin_lock(&GlobalMid_Lock);
> +                       if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
> +                               swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
> +                       spin_unlock(&GlobalMid_Lock);
> +               }
> +               break;
> +       case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
> +               cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
> +               break;
> +       }
> +       return 0;
> +}
> +
> +int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
> +{
> +       struct cifs_swn_reg *swnreg;
> +       char name[256];
> +       int type;
> +
> +       if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
> +               int swnreg_id;
> +
> +               swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
> +               mutex_lock(&cifs_swnreg_idr_mutex);
> +               swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
> +               mutex_unlock(&cifs_swnreg_idr_mutex);
> +               if (swnreg == NULL) {
> +                       cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
> +                       return -EINVAL;
> +               }
> +       } else {
> +               cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
> +               type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
> +       } else {
> +               cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       switch (type) {
> +       case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: {
> +               int state;
> +
> +               if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) {
> +                       nla_strlcpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME],
> +                                       sizeof(name));
> +               } else {
> +                       cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
> +                       return -EINVAL;
> +               }
> +               if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
> +                       state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
> +               } else {
> +                       cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
> +                       return -EINVAL;
> +               }
> +               return cifs_swn_resource_state_changed(swnreg, name, state);
> +       }
> +       default:
> +               cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
>  int cifs_swn_register(struct cifs_tcon *tcon)
>  {
>         struct cifs_swn_reg *swnreg;
> diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
> index 69c7bd1035da..7ef9ecedbd05 100644
> --- a/fs/cifs/cifs_swn.h
> +++ b/fs/cifs/cifs_swn.h
> @@ -9,9 +9,13 @@
>  #define _CIFS_SWN_H
>
>  struct cifs_tcon;
> +struct sk_buff;
> +struct genl_info;
>
>  extern int cifs_swn_register(struct cifs_tcon *tcon);
>
>  extern int cifs_swn_unregister(struct cifs_tcon *tcon);
>
> +extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
> +
>  #endif /* _CIFS_SWN_H */
> diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c
> index 83008a56def5..5aaabe4cc0a7 100644
> --- a/fs/cifs/netlink.c
> +++ b/fs/cifs/netlink.c
> @@ -11,6 +11,7 @@
>  #include "netlink.h"
>  #include "cifsglob.h"
>  #include "cifs_debug.h"
> +#include "cifs_swn.h"
>
>  static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
>         [CIFS_GENL_ATTR_SWN_REGISTRATION_ID]    = { .type = NLA_U32 },
> @@ -24,9 +25,17 @@ static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
>         [CIFS_GENL_ATTR_SWN_USER_NAME]          = { .type = NLA_STRING },
>         [CIFS_GENL_ATTR_SWN_PASSWORD]           = { .type = NLA_STRING },
>         [CIFS_GENL_ATTR_SWN_DOMAIN_NAME]        = { .type = NLA_STRING },
> +       [CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]  = { .type = NLA_U32 },
> +       [CIFS_GENL_ATTR_SWN_RESOURCE_STATE]     = { .type = NLA_U32 },
> +       [CIFS_GENL_ATTR_SWN_RESOURCE_NAME]      = { .type = NLA_STRING},
>  };
>
>  static struct genl_ops cifs_genl_ops[] = {
> +       {
> +               .cmd = CIFS_GENL_CMD_SWN_NOTIFY,
> +               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +               .doit = cifs_swn_notify,
> +       },
>  };
>
>  static const struct genl_multicast_group cifs_genl_mcgrps[] = {
> diff --git a/include/uapi/linux/cifs/cifs_netlink.h b/include/uapi/linux/cifs/cifs_netlink.h
> index 5662e2774513..da3107582f49 100644
> --- a/include/uapi/linux/cifs/cifs_netlink.h
> +++ b/include/uapi/linux/cifs/cifs_netlink.h
> @@ -31,6 +31,9 @@ enum cifs_genl_attributes {
>         CIFS_GENL_ATTR_SWN_USER_NAME,
>         CIFS_GENL_ATTR_SWN_PASSWORD,
>         CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
> +       CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE,
> +       CIFS_GENL_ATTR_SWN_RESOURCE_STATE,
> +       CIFS_GENL_ATTR_SWN_RESOURCE_NAME,
>         __CIFS_GENL_ATTR_MAX,
>  };
>  #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
> @@ -39,8 +42,22 @@ enum cifs_genl_commands {
>         CIFS_GENL_CMD_UNSPEC,
>         CIFS_GENL_CMD_SWN_REGISTER,
>         CIFS_GENL_CMD_SWN_UNREGISTER,
> +       CIFS_GENL_CMD_SWN_NOTIFY,
>         __CIFS_GENL_CMD_MAX
>  };
>  #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
>
> +enum cifs_swn_notification_type {
> +       CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE = 0x01,
> +       CIFS_SWN_NOTIFICATION_CLIENT_MOVE        = 0x02,
> +       CIFS_SWN_NOTIFICATION_SHARE_MOVE         = 0x03,
> +       CIFS_SWN_NOTIFICATION_IP_CHANGE  = 0x04,
> +};
> +
> +enum cifs_swn_resource_state {
> +       CIFS_SWN_RESOURCE_STATE_UNKNOWN     = 0x00,
> +       CIFS_SWN_RESOURCE_STATE_AVAILABLE   = 0x01,
> +       CIFS_SWN_RESOURCE_STATE_UNAVAILABLE = 0xFF
> +};
> +
>  #endif /* _UAPILINUX_CIFS_NETLINK_H */
> --
> 2.29.2
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 07/11] cifs: Add witness information to debug data dump
  2020-11-30 18:02 ` [PATCH v4 07/11] cifs: Add witness information to debug data dump Samuel Cabrero
@ 2020-12-12  5:57   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  5:57 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

tentatively merged into cifs-2.6.git for-next, let me know if any
changes needed to be made to it.

On Mon, Nov 30, 2020 at 12:05 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> + Indicate if witness feature is supported
> + Indicate if witness is used when dumping tcons
> + Dumps witness registrations. Example:
>   Witness registrations:
>   Id: 1 Refs: 1 Network name: 'fs.fover.ad'(y) Share name: 'share1'(y) \
>     Ip address: 192.168.103.200(n)
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cifs_debug.c | 13 +++++++++++++
>  fs/cifs/cifs_swn.c   | 35 +++++++++++++++++++++++++++++++++++
>  fs/cifs/cifs_swn.h   |  2 ++
>  3 files changed, 50 insertions(+)
>
> diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
> index 53588d7517b4..b231dcf1d1f9 100644
> --- a/fs/cifs/cifs_debug.c
> +++ b/fs/cifs/cifs_debug.c
> @@ -23,6 +23,9 @@
>  #ifdef CONFIG_CIFS_SMB_DIRECT
>  #include "smbdirect.h"
>  #endif
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +#include "cifs_swn.h"
> +#endif
>
>  void
>  cifs_dump_mem(char *label, void *data, int length)
> @@ -115,6 +118,10 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
>                 seq_printf(m, " POSIX Extensions");
>         if (tcon->ses->server->ops->dump_share_caps)
>                 tcon->ses->server->ops->dump_share_caps(m, tcon);
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       if (tcon->use_witness)
> +               seq_puts(m, " Witness");
> +#endif
>
>         if (tcon->need_reconnect)
>                 seq_puts(m, "\tDISCONNECTED ");
> @@ -262,6 +269,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
>         seq_printf(m, ",XATTR");
>  #endif
>         seq_printf(m, ",ACL");
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       seq_puts(m, ",WITNESS");
> +#endif
>         seq_putc(m, '\n');
>         seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
>         seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
> @@ -462,6 +472,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
>         spin_unlock(&cifs_tcp_ses_lock);
>         seq_putc(m, '\n');
>
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       cifs_swn_dump(m);
> +#endif
>         /* BB add code to dump additional info such as TCP session info now */
>         return 0;
>  }
> diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
> index 63b0764af5d5..140a53a19aa0 100644
> --- a/fs/cifs/cifs_swn.c
> +++ b/fs/cifs/cifs_swn.c
> @@ -505,3 +505,38 @@ int cifs_swn_unregister(struct cifs_tcon *tcon)
>
>         return 0;
>  }
> +
> +void cifs_swn_dump(struct seq_file *m)
> +{
> +       struct cifs_swn_reg *swnreg;
> +       struct sockaddr_in *sa;
> +       struct sockaddr_in6 *sa6;
> +       int id;
> +
> +       seq_puts(m, "Witness registrations:");
> +
> +       mutex_lock(&cifs_swnreg_idr_mutex);
> +       idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
> +               seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
> +                               id, kref_read(&swnreg->ref_count),
> +                               swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
> +                               swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
> +               switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
> +               case AF_INET:
> +                       sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
> +                       seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
> +                       break;
> +               case AF_INET6:
> +                       sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
> +                       seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
> +                       if (sa6->sin6_scope_id)
> +                               seq_printf(m, "%%%u", sa6->sin6_scope_id);
> +                       break;
> +               default:
> +                       seq_puts(m, "(unknown)");
> +               }
> +               seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
> +       }
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +       seq_puts(m, "\n");
> +}
> diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
> index 7ef9ecedbd05..13b25cdc9295 100644
> --- a/fs/cifs/cifs_swn.h
> +++ b/fs/cifs/cifs_swn.h
> @@ -18,4 +18,6 @@ extern int cifs_swn_unregister(struct cifs_tcon *tcon);
>
>  extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
>
> +extern void cifs_swn_dump(struct seq_file *m);
> +
>  #endif /* _CIFS_SWN_H */
> --
> 2.29.2
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task
  2020-11-30 18:02 ` [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task Samuel Cabrero
@ 2020-12-12  6:00   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  6:00 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

tentatively merged into cifs-2.6.git for-next. Let me know if any
changes need to be made to it

On Mon, Nov 30, 2020 at 12:06 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> If the daemon starts after mounting a share, or if it crashes, this
> provides a mechanism to register again.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/cifs_swn.c | 15 +++++++++++++++
>  fs/cifs/cifs_swn.h |  2 ++
>  fs/cifs/connect.c  |  5 +++++
>  3 files changed, 22 insertions(+)
>
> diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c
> index 140a53a19aa0..642c9eedc8ab 100644
> --- a/fs/cifs/cifs_swn.c
> +++ b/fs/cifs/cifs_swn.c
> @@ -540,3 +540,18 @@ void cifs_swn_dump(struct seq_file *m)
>         mutex_unlock(&cifs_swnreg_idr_mutex);
>         seq_puts(m, "\n");
>  }
> +
> +void cifs_swn_check(void)
> +{
> +       struct cifs_swn_reg *swnreg;
> +       int id;
> +       int ret;
> +
> +       mutex_lock(&cifs_swnreg_idr_mutex);
> +       idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
> +               ret = cifs_swn_send_register_message(swnreg);
> +               if (ret < 0)
> +                       cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
> +       }
> +       mutex_unlock(&cifs_swnreg_idr_mutex);
> +}
> diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h
> index 13b25cdc9295..236ecd4959d5 100644
> --- a/fs/cifs/cifs_swn.h
> +++ b/fs/cifs/cifs_swn.h
> @@ -20,4 +20,6 @@ extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
>
>  extern void cifs_swn_dump(struct seq_file *m);
>
> +extern void cifs_swn_check(void);
> +
>  #endif /* _CIFS_SWN_H */
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 7fbb201b42c3..a298518bebb2 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -613,6 +613,11 @@ cifs_echo_request(struct work_struct *work)
>                 cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
>                          server->hostname);
>
> +#ifdef CONFIG_CIFS_SWN_UPCALL
> +       /* Check witness registrations */
> +       cifs_swn_check();
> +#endif
> +
>  requeue_echo:
>         queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
>  }
> --
> 2.29.2
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled
  2020-11-30 18:02 ` [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled Samuel Cabrero
@ 2020-12-12  6:07   ` Steve French
  0 siblings, 0 replies; 23+ messages in thread
From: Steve French @ 2020-12-12  6:07 UTC (permalink / raw)
  To: Samuel Cabrero; +Cc: CIFS

tentatively merged into cifs-2.6.git for-next, let me know if any
changes need to be made to it.

On Mon, Nov 30, 2020 at 12:05 PM Samuel Cabrero <scabrero@suse.de> wrote:
>
> Some witness notifications, like client move, tell the client to
> reconnect to a specific IP address. In this situation the DFS failover
> code path has to be skipped so clean up as much as possible the
> cifs_reconnect() code.
>
> Signed-off-by: Samuel Cabrero <scabrero@suse.de>
> ---
>  fs/cifs/connect.c | 21 ++++++++-------------
>  1 file changed, 8 insertions(+), 13 deletions(-)
>
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index a298518bebb2..3af88711643b 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -296,7 +296,7 @@ static void cifs_prune_tlinks(struct work_struct *work);
>   * This should be called with server->srv_mutex held.
>   */
>  #ifdef CONFIG_CIFS_DFS_UPCALL
> -static int reconn_set_ipaddr(struct TCP_Server_Info *server)
> +static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>  {
>         int rc;
>         int len;
> @@ -331,14 +331,7 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server)
>
>         return !rc ? -1 : 0;
>  }
> -#else
> -static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
> -{
> -       return 0;
> -}
> -#endif
>
> -#ifdef CONFIG_CIFS_DFS_UPCALL
>  /* These functions must be called with server->srv_mutex held */
>  static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
>                                        struct cifs_sb_info *cifs_sb,
> @@ -346,6 +339,7 @@ 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;
> @@ -370,6 +364,12 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
>                          "%s: failed to extract hostname from target: %ld\n",
>                          __func__, PTR_ERR(server->hostname));
>         }
> +
> +       rc = reconn_set_ipaddr_from_hostname(server);
> +       if (rc) {
> +               cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
> +                        __func__, rc);
> +       }
>  }
>
>  static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
> @@ -528,11 +528,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
>                  */
>                 reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
>  #endif
> -               rc = reconn_set_ipaddr(server);
> -               if (rc) {
> -                       cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
> -                                __func__, rc);
> -               }
>
>                 if (cifs_rdma_enabled(server))
>                         rc = smbd_reconnect(server);
> --
> 2.29.2
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2020-12-12  6:09 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-30 18:02 [PATCH v4 00/11] Witness protocol support for transparent failover Samuel Cabrero
2020-11-30 18:02 ` [PATCH v4 01/11] cifs: Make extract_hostname function public Samuel Cabrero
2020-12-12  3:42   ` Steve French
2020-11-30 18:02 ` [PATCH v4 02/11] cifs: Make extract_sharename " Samuel Cabrero
2020-12-12  5:50   ` Steve French
2020-11-30 18:02 ` [PATCH v4 03/11] cifs: Register generic netlink family Samuel Cabrero
2020-12-12  5:51   ` Steve French
2020-11-30 18:02 ` [PATCH v4 04/11] cifs: add witness mount option and data structs Samuel Cabrero
2020-12-12  5:07   ` Steve French
2020-11-30 18:02 ` [PATCH v4 05/11] cifs: Send witness register and unregister commands to userspace daemon Samuel Cabrero
2020-12-12  5:52   ` Steve French
2020-11-30 18:02 ` [PATCH v4 06/11] cifs: Set witness notification handler for messages from " Samuel Cabrero
2020-12-12  5:55   ` Steve French
2020-11-30 18:02 ` [PATCH v4 07/11] cifs: Add witness information to debug data dump Samuel Cabrero
2020-12-12  5:57   ` Steve French
2020-11-30 18:02 ` [PATCH v4 08/11] cifs: Send witness register messages to userspace daemon in echo task Samuel Cabrero
2020-12-12  6:00   ` Steve French
2020-11-30 18:02 ` [PATCH v4 09/11] cifs: Simplify reconnect code when dfs upcall is enabled Samuel Cabrero
2020-12-12  6:07   ` Steve French
2020-11-30 18:02 ` [PATCH v4 10/11] cifs: Handle witness client move notification Samuel Cabrero
2020-11-30 18:02 ` [PATCH v4 11/11] cifs: Handle witness share moved notification Samuel Cabrero
2020-12-09 12:36 ` [PATCH v4 00/11] Witness protocol support for transparent failover Aurélien Aptel
2020-12-09 15:06   ` Paulo Alcantara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.