File Index
Files and directories
Sign in
FILE

cifs_swn.c

File preview with syntax highlighting.

linux-6.19.14/fs/smb/client/cifs_swn.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Witness Service client for CIFS
  4 *
  5 * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
  6 */
  7
  8#include <linux/kref.h>
  9#include <net/genetlink.h>
 10#include <uapi/linux/cifs/cifs_netlink.h>
 11
 12#include "cifs_swn.h"
 13#include "cifsglob.h"
 14#include "cifsproto.h"
 15#include "fscache.h"
 16#include "cifs_debug.h"
 17#include "netlink.h"
 18
 19static DEFINE_IDR(cifs_swnreg_idr);
 20static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
 21
 22struct cifs_swn_reg {
 23	int id;
 24	struct kref ref_count;
 25
 26	const char *net_name;
 27	const char *share_name;
 28	bool net_name_notify;
 29	bool share_name_notify;
 30	bool ip_notify;
 31
 32	struct cifs_tcon *tcon;
 33};
 34
 35static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
 36{
 37	int ret;
 38
 39	ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH);
 40	if (ret < 0)
 41		return ret;
 42
 43	return 0;
 44}
 45
 46static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
 47{
 48	int ret;
 49
 50	if (tcon->ses->user_name != NULL) {
 51		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name);
 52		if (ret < 0)
 53			return ret;
 54	}
 55
 56	if (tcon->ses->password != NULL) {
 57		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password);
 58		if (ret < 0)
 59			return ret;
 60	}
 61
 62	if (tcon->ses->domainName != NULL) {
 63		ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName);
 64		if (ret < 0)
 65			return ret;
 66	}
 67
 68	return 0;
 69}
 70
 71/*
 72 * Sends a register message to the userspace daemon based on the registration.
 73 * The authentication information to connect to the witness service is bundled
 74 * into the message.
 75 */
 76static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
 77{
 78	struct sk_buff *skb;
 79	struct genlmsghdr *hdr;
 80	enum securityEnum authtype;
 81	struct sockaddr_storage *addr;
 82	int ret;
 83
 84	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 85	if (!skb)
 86		return -ENOMEM;
 87
 88	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
 89	if (hdr == NULL) {
 90		ret = -ENOMEM;
 91		goto nlmsg_fail;
 92	}
 93
 94	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
 95	if (ret < 0)
 96		goto nlmsg_fail;
 97
 98	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
 99	if (ret < 0)
100		goto nlmsg_fail;
101
102	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
103	if (ret < 0)
104		goto nlmsg_fail;
105
106	/*
107	 * If there is an address stored use it instead of the server address, because we are
108	 * in the process of reconnecting to it after a share has been moved or we have been
109	 * told to switch to it (client move message). In these cases we unregister from the
110	 * server address and register to the new address when we receive the notification.
111	 */
112	if (swnreg->tcon->ses->server->use_swn_dstaddr)
113		addr = &swnreg->tcon->ses->server->swn_dstaddr;
114	else
115		addr = &swnreg->tcon->ses->server->dstaddr;
116
117	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
118	if (ret < 0)
119		goto nlmsg_fail;
120
121	if (swnreg->net_name_notify) {
122		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
123		if (ret < 0)
124			goto nlmsg_fail;
125	}
126
127	if (swnreg->share_name_notify) {
128		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
129		if (ret < 0)
130			goto nlmsg_fail;
131	}
132
133	if (swnreg->ip_notify) {
134		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
135		if (ret < 0)
136			goto nlmsg_fail;
137	}
138
139	authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
140	switch (authtype) {
141	case Kerberos:
142		ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
143		if (ret < 0) {
144			cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
145			goto nlmsg_fail;
146		}
147		break;
148	case NTLMv2:
149	case RawNTLMSSP:
150		ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
151		if (ret < 0) {
152			cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
153			goto nlmsg_fail;
154		}
155		break;
156	default:
157		cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
158		ret = -EINVAL;
159		goto nlmsg_fail;
160	}
161
162	genlmsg_end(skb, hdr);
163	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
164
165	cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
166			swnreg->net_name, swnreg->id);
167
168	return 0;
169
170nlmsg_fail:
171	genlmsg_cancel(skb, hdr);
172	nlmsg_free(skb);
173	return ret;
174}
175
176/*
177 * Sends an uregister message to the userspace daemon based on the registration
178 */
179static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
180{
181	struct sk_buff *skb;
182	struct genlmsghdr *hdr;
183	int ret;
184
185	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
186	if (skb == NULL)
187		return -ENOMEM;
188
189	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
190	if (hdr == NULL) {
191		ret = -ENOMEM;
192		goto nlmsg_fail;
193	}
194
195	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
196	if (ret < 0)
197		goto nlmsg_fail;
198
199	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
200	if (ret < 0)
201		goto nlmsg_fail;
202
203	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
204	if (ret < 0)
205		goto nlmsg_fail;
206
207	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
208			&swnreg->tcon->ses->server->dstaddr);
209	if (ret < 0)
210		goto nlmsg_fail;
211
212	if (swnreg->net_name_notify) {
213		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
214		if (ret < 0)
215			goto nlmsg_fail;
216	}
217
218	if (swnreg->share_name_notify) {
219		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
220		if (ret < 0)
221			goto nlmsg_fail;
222	}
223
224	if (swnreg->ip_notify) {
225		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
226		if (ret < 0)
227			goto nlmsg_fail;
228	}
229
230	genlmsg_end(skb, hdr);
231	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
232
233	cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
234			swnreg->net_name, swnreg->id);
235
236	return 0;
237
238nlmsg_fail:
239	genlmsg_cancel(skb, hdr);
240	nlmsg_free(skb);
241	return ret;
242}
243
244/*
245 * Try to find a matching registration for the tcon's server name and share name.
246 * Calls to this function must be protected by cifs_swnreg_idr_mutex.
247 * TODO Try to avoid memory allocations
248 */
249static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
250{
251	struct cifs_swn_reg *swnreg;
252	int id;
253	const char *share_name;
254	const char *net_name;
255
256	net_name = extract_hostname(tcon->tree_name);
257	if (IS_ERR(net_name)) {
258		int ret;
259
260		ret = PTR_ERR(net_name);
261		cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
262				__func__, tcon->tree_name, ret);
263		return ERR_PTR(-EINVAL);
264	}
265
266	share_name = extract_sharename(tcon->tree_name);
267	if (IS_ERR(share_name)) {
268		int ret;
269
270		ret = PTR_ERR(share_name);
271		cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
272				__func__, tcon->tree_name, ret);
273		kfree(net_name);
274		return ERR_PTR(-EINVAL);
275	}
276
277	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
278		if (strcasecmp(swnreg->net_name, net_name) != 0
279		    || strcasecmp(swnreg->share_name, share_name) != 0) {
280			continue;
281		}
282
283		cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
284				swnreg->share_name);
285
286		kfree(net_name);
287		kfree(share_name);
288
289		return swnreg;
290	}
291
292	kfree(net_name);
293	kfree(share_name);
294
295	return ERR_PTR(-EEXIST);
296}
297
298/*
299 * Get a registration for the tcon's server and share name, allocating a new one if it does not
300 * exists
301 */
302static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
303{
304	struct cifs_swn_reg *reg = NULL;
305	int ret;
306
307	mutex_lock(&cifs_swnreg_idr_mutex);
308
309	/* Check if we are already registered for this network and share names */
310	reg = cifs_find_swn_reg(tcon);
311	if (!IS_ERR(reg)) {
312		kref_get(&reg->ref_count);
313		goto unlock;
314	} else if (PTR_ERR(reg) != -EEXIST) {
315		goto unlock;
316	}
317
318	reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
319	if (reg == NULL) {
320		ret = -ENOMEM;
321		goto fail_unlock;
322	}
323
324	kref_init(&reg->ref_count);
325
326	reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
327	if (reg->id < 0) {
328		cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
329		ret = reg->id;
330		goto fail;
331	}
332
333	reg->net_name = extract_hostname(tcon->tree_name);
334	if (IS_ERR(reg->net_name)) {
335		ret = PTR_ERR(reg->net_name);
336		cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
337		goto fail_idr;
338	}
339
340	reg->share_name = extract_sharename(tcon->tree_name);
341	if (IS_ERR(reg->share_name)) {
342		ret = PTR_ERR(reg->share_name);
343		cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
344		goto fail_net_name;
345	}
346
347	reg->net_name_notify = true;
348	reg->share_name_notify = true;
349	reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
350
351	reg->tcon = tcon;
352unlock:
353	mutex_unlock(&cifs_swnreg_idr_mutex);
354
355	return reg;
356
357fail_net_name:
358	kfree(reg->net_name);
359fail_idr:
360	idr_remove(&cifs_swnreg_idr, reg->id);
361fail:
362	kfree(reg);
363fail_unlock:
364	mutex_unlock(&cifs_swnreg_idr_mutex);
365	return ERR_PTR(ret);
366}
367
368static void cifs_swn_reg_release(struct kref *ref)
369{
370	struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
371	int ret;
372
373	ret = cifs_swn_send_unregister_message(swnreg);
374	if (ret < 0)
375		cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
376
377	idr_remove(&cifs_swnreg_idr, swnreg->id);
378	kfree(swnreg->net_name);
379	kfree(swnreg->share_name);
380	kfree(swnreg);
381}
382
383static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
384{
385	mutex_lock(&cifs_swnreg_idr_mutex);
386	kref_put(&swnreg->ref_count, cifs_swn_reg_release);
387	mutex_unlock(&cifs_swnreg_idr_mutex);
388}
389
390static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
391{
392	switch (state) {
393	case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
394		cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
395		cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
396		break;
397	case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
398		cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
399		cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
400		break;
401	case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
402		cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
403		break;
404	}
405	return 0;
406}
407
408static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
409{
410	if (addr1->ss_family != addr2->ss_family)
411		return false;
412
413	if (addr1->ss_family == AF_INET) {
414		return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr,
415				&((const struct sockaddr_in *)addr2)->sin_addr,
416				sizeof(struct in_addr)) == 0);
417	}
418
419	if (addr1->ss_family == AF_INET6) {
420		return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr,
421				&((const struct sockaddr_in6 *)addr2)->sin6_addr,
422				sizeof(struct in6_addr)) == 0);
423	}
424
425	return false;
426}
427
428static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new,
429				   const struct sockaddr_storage *old,
430				   struct sockaddr_storage *dst)
431{
432	__be16 port = cpu_to_be16(CIFS_PORT);
433
434	if (old->ss_family == AF_INET) {
435		struct sockaddr_in *ipv4 = (struct sockaddr_in *)old;
436
437		port = ipv4->sin_port;
438	} else if (old->ss_family == AF_INET6) {
439		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old;
440
441		port = ipv6->sin6_port;
442	}
443
444	if (new->ss_family == AF_INET) {
445		struct sockaddr_in *ipv4 = (struct sockaddr_in *)new;
446
447		ipv4->sin_port = port;
448	} else if (new->ss_family == AF_INET6) {
449		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new;
450
451		ipv6->sin6_port = port;
452	}
453
454	*dst = *new;
455
456	return 0;
457}
458
459static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
460{
461	int ret = 0;
462
463	/* Store the reconnect address */
464	cifs_server_lock(tcon->ses->server);
465	if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr))
466		goto unlock;
467
468	ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr,
469				      &tcon->ses->server->swn_dstaddr);
470	if (ret < 0) {
471		cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret);
472		goto unlock;
473	}
474	tcon->ses->server->use_swn_dstaddr = true;
475
476	/*
477	 * Unregister to stop receiving notifications for the old IP address.
478	 */
479	ret = cifs_swn_unregister(tcon);
480	if (ret < 0) {
481		cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
482			 __func__, ret);
483		goto unlock;
484	}
485
486	/*
487	 * And register to receive notifications for the new IP address now that we have
488	 * stored the new address.
489	 */
490	ret = cifs_swn_register(tcon);
491	if (ret < 0) {
492		cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n",
493			 __func__, ret);
494		goto unlock;
495	}
496
497	cifs_signal_cifsd_for_reconnect(tcon->ses->server, false);
498
499unlock:
500	cifs_server_unlock(tcon->ses->server);
501
502	return ret;
503}
504
505static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
506{
507	struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
508	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
509
510	if (addr->ss_family == AF_INET)
511		cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr);
512	else if (addr->ss_family == AF_INET6)
513		cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
514
515	return cifs_swn_reconnect(swnreg->tcon, addr);
516}
517
518int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
519{
520	struct cifs_swn_reg *swnreg;
521	char name[256];
522	int type;
523
524	if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
525		int swnreg_id;
526
527		swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
528		mutex_lock(&cifs_swnreg_idr_mutex);
529		swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
530		mutex_unlock(&cifs_swnreg_idr_mutex);
531		if (swnreg == NULL) {
532			cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
533			return -EINVAL;
534		}
535	} else {
536		cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
537		return -EINVAL;
538	}
539
540	if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
541		type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
542	} else {
543		cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
544		return -EINVAL;
545	}
546
547	switch (type) {
548	case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: {
549		int state;
550
551		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) {
552			nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME],
553					sizeof(name));
554		} else {
555			cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
556			return -EINVAL;
557		}
558		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
559			state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
560		} else {
561			cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
562			return -EINVAL;
563		}
564		return cifs_swn_resource_state_changed(swnreg, name, state);
565	}
566	case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
567		struct sockaddr_storage addr;
568
569		if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) {
570			nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
571		} else {
572			cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
573			return -EINVAL;
574		}
575		return cifs_swn_client_move(swnreg, &addr);
576	}
577	default:
578		cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
579		break;
580	}
581
582	return 0;
583}
584
585int cifs_swn_register(struct cifs_tcon *tcon)
586{
587	struct cifs_swn_reg *swnreg;
588	int ret;
589
590	swnreg = cifs_get_swn_reg(tcon);
591	if (IS_ERR(swnreg))
592		return PTR_ERR(swnreg);
593
594	ret = cifs_swn_send_register_message(swnreg);
595	if (ret < 0) {
596		cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
597		/* Do not put the swnreg or return error, the echo task will retry */
598	}
599
600	return 0;
601}
602
603int cifs_swn_unregister(struct cifs_tcon *tcon)
604{
605	struct cifs_swn_reg *swnreg;
606
607	mutex_lock(&cifs_swnreg_idr_mutex);
608
609	swnreg = cifs_find_swn_reg(tcon);
610	if (IS_ERR(swnreg)) {
611		mutex_unlock(&cifs_swnreg_idr_mutex);
612		return PTR_ERR(swnreg);
613	}
614
615	mutex_unlock(&cifs_swnreg_idr_mutex);
616
617	cifs_put_swn_reg(swnreg);
618
619	return 0;
620}
621
622void cifs_swn_dump(struct seq_file *m)
623{
624	struct cifs_swn_reg *swnreg;
625	struct sockaddr_in *sa;
626	struct sockaddr_in6 *sa6;
627	int id;
628
629	seq_puts(m, "Witness registrations:");
630
631	mutex_lock(&cifs_swnreg_idr_mutex);
632	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
633		seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
634				id, kref_read(&swnreg->ref_count),
635				swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
636				swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
637		switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
638		case AF_INET:
639			sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
640			seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
641			break;
642		case AF_INET6:
643			sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
644			seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
645			if (sa6->sin6_scope_id)
646				seq_printf(m, "%%%u", sa6->sin6_scope_id);
647			break;
648		default:
649			seq_puts(m, "(unknown)");
650		}
651		seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
652	}
653	mutex_unlock(&cifs_swnreg_idr_mutex);
654	seq_puts(m, "\n");
655}
656
657void cifs_swn_check(void)
658{
659	struct cifs_swn_reg *swnreg;
660	int id;
661	int ret;
662
663	mutex_lock(&cifs_swnreg_idr_mutex);
664	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
665		ret = cifs_swn_send_register_message(swnreg);
666		if (ret < 0)
667			cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
668	}
669	mutex_unlock(&cifs_swnreg_idr_mutex);
670}
~/ Read-only file preview.