Commit 121d947d authored by Samuel Cabrero's avatar Samuel Cabrero Committed by Steve French

cifs: Handle witness client move notification

This message is sent to tell a client to close its current connection
and connect to the specified address.
Signed-off-by: default avatarSamuel Cabrero <scabrero@suse.de>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent af1e40d9
......@@ -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;
......
......@@ -687,6 +687,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 {
......
......@@ -312,6 +312,13 @@ 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
......@@ -321,6 +328,10 @@ cifs_reconnect(struct TCP_Server_Info *server)
reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
#endif
#ifdef CONFIG_CIFS_SWN_UPCALL
}
#endif
if (cifs_rdma_enabled(server))
rc = smbd_reconnect(server);
else
......@@ -336,6 +347,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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment