162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/lockd/svcsubs.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Various support routines for the NLM server.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/string.h>
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci#include <linux/in.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/sunrpc/svc.h>
1762306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
1862306a36Sopenharmony_ci#include <linux/lockd/lockd.h>
1962306a36Sopenharmony_ci#include <linux/lockd/share.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/mount.h>
2262306a36Sopenharmony_ci#include <uapi/linux/nfs2.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define NLMDBG_FACILITY		NLMDBG_SVCSUBS
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Global file hash table
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci#define FILE_HASH_BITS		7
3162306a36Sopenharmony_ci#define FILE_NRHASH		(1<<FILE_HASH_BITS)
3262306a36Sopenharmony_cistatic struct hlist_head	nlm_files[FILE_NRHASH];
3362306a36Sopenharmony_cistatic DEFINE_MUTEX(nlm_file_mutex);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_SUNRPC_DEBUG
3662306a36Sopenharmony_cistatic inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	u32 *fhp = (u32*)f->data;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* print the first 32 bytes of the fh */
4162306a36Sopenharmony_ci	dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
4262306a36Sopenharmony_ci		msg, fhp[0], fhp[1], fhp[2], fhp[3],
4362306a36Sopenharmony_ci		fhp[4], fhp[5], fhp[6], fhp[7]);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct inode *inode = nlmsvc_file_inode(file);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	dprintk("lockd: %s %s/%ld\n",
5162306a36Sopenharmony_ci		msg, inode->i_sb->s_id, inode->i_ino);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci#else
5462306a36Sopenharmony_cistatic inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic inline unsigned int file_hash(struct nfs_fh *f)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	unsigned int tmp=0;
6862306a36Sopenharmony_ci	int i;
6962306a36Sopenharmony_ci	for (i=0; i<NFS2_FHSIZE;i++)
7062306a36Sopenharmony_ci		tmp += f->data[i];
7162306a36Sopenharmony_ci	return tmp & (FILE_NRHASH - 1);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciint lock_to_openmode(struct file_lock *lock)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Open the file. Note that if we're reexporting, for example,
8162306a36Sopenharmony_ci * this could block the lockd thread for a while.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * We have to make sure we have the right credential to open
8462306a36Sopenharmony_ci * the file.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic __be32 nlm_do_fopen(struct svc_rqst *rqstp,
8762306a36Sopenharmony_ci			   struct nlm_file *file, int mode)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct file **fp = &file->f_file[mode];
9062306a36Sopenharmony_ci	__be32	nfserr;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (*fp)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci	nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
9562306a36Sopenharmony_ci	if (nfserr)
9662306a36Sopenharmony_ci		dprintk("lockd: open failed (error %d)\n", nfserr);
9762306a36Sopenharmony_ci	return nfserr;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * Lookup file info. If it doesn't exist, create a file info struct
10262306a36Sopenharmony_ci * and open a (VFS) file for the given inode.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ci__be32
10562306a36Sopenharmony_cinlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
10662306a36Sopenharmony_ci					struct nlm_lock *lock)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct nlm_file	*file;
10962306a36Sopenharmony_ci	unsigned int	hash;
11062306a36Sopenharmony_ci	__be32		nfserr;
11162306a36Sopenharmony_ci	int		mode;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	hash = file_hash(&lock->fh);
11662306a36Sopenharmony_ci	mode = lock_to_openmode(&lock->fl);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Lock file table */
11962306a36Sopenharmony_ci	mutex_lock(&nlm_file_mutex);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	hlist_for_each_entry(file, &nlm_files[hash], f_list)
12262306a36Sopenharmony_ci		if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
12362306a36Sopenharmony_ci			mutex_lock(&file->f_mutex);
12462306a36Sopenharmony_ci			nfserr = nlm_do_fopen(rqstp, file, mode);
12562306a36Sopenharmony_ci			mutex_unlock(&file->f_mutex);
12662306a36Sopenharmony_ci			goto found;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	nlm_debug_print_fh("creating file for", &lock->fh);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	nfserr = nlm_lck_denied_nolocks;
13162306a36Sopenharmony_ci	file = kzalloc(sizeof(*file), GFP_KERNEL);
13262306a36Sopenharmony_ci	if (!file)
13362306a36Sopenharmony_ci		goto out_free;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
13662306a36Sopenharmony_ci	mutex_init(&file->f_mutex);
13762306a36Sopenharmony_ci	INIT_HLIST_NODE(&file->f_list);
13862306a36Sopenharmony_ci	INIT_LIST_HEAD(&file->f_blocks);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	nfserr = nlm_do_fopen(rqstp, file, mode);
14162306a36Sopenharmony_ci	if (nfserr)
14262306a36Sopenharmony_ci		goto out_unlock;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	hlist_add_head(&file->f_list, &nlm_files[hash]);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cifound:
14762306a36Sopenharmony_ci	dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
14862306a36Sopenharmony_ci	*result = file;
14962306a36Sopenharmony_ci	file->f_count++;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciout_unlock:
15262306a36Sopenharmony_ci	mutex_unlock(&nlm_file_mutex);
15362306a36Sopenharmony_ci	return nfserr;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciout_free:
15662306a36Sopenharmony_ci	kfree(file);
15762306a36Sopenharmony_ci	goto out_unlock;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci * Delete a file after having released all locks, blocks and shares
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic inline void
16462306a36Sopenharmony_cinlm_delete_file(struct nlm_file *file)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	nlm_debug_print_file("closing file", file);
16762306a36Sopenharmony_ci	if (!hlist_unhashed(&file->f_list)) {
16862306a36Sopenharmony_ci		hlist_del(&file->f_list);
16962306a36Sopenharmony_ci		if (file->f_file[O_RDONLY])
17062306a36Sopenharmony_ci			nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
17162306a36Sopenharmony_ci		if (file->f_file[O_WRONLY])
17262306a36Sopenharmony_ci			nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
17362306a36Sopenharmony_ci		kfree(file);
17462306a36Sopenharmony_ci	} else {
17562306a36Sopenharmony_ci		printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct file_lock lock;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	locks_init_lock(&lock);
18462306a36Sopenharmony_ci	lock.fl_type  = F_UNLCK;
18562306a36Sopenharmony_ci	lock.fl_start = 0;
18662306a36Sopenharmony_ci	lock.fl_end   = OFFSET_MAX;
18762306a36Sopenharmony_ci	lock.fl_owner = fl->fl_owner;
18862306a36Sopenharmony_ci	lock.fl_pid   = fl->fl_pid;
18962306a36Sopenharmony_ci	lock.fl_flags = FL_POSIX;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	lock.fl_file = file->f_file[O_RDONLY];
19262306a36Sopenharmony_ci	if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
19362306a36Sopenharmony_ci		goto out_err;
19462306a36Sopenharmony_ci	lock.fl_file = file->f_file[O_WRONLY];
19562306a36Sopenharmony_ci	if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
19662306a36Sopenharmony_ci		goto out_err;
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ciout_err:
19962306a36Sopenharmony_ci	pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
20062306a36Sopenharmony_ci	return 1;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * Loop over all locks on the given file and perform the specified
20562306a36Sopenharmony_ci * action.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic int
20862306a36Sopenharmony_cinlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
20962306a36Sopenharmony_ci			nlm_host_match_fn_t match)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct inode	 *inode = nlmsvc_file_inode(file);
21262306a36Sopenharmony_ci	struct file_lock *fl;
21362306a36Sopenharmony_ci	struct file_lock_context *flctx = locks_inode_context(inode);
21462306a36Sopenharmony_ci	struct nlm_host	 *lockhost;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!flctx || list_empty_careful(&flctx->flc_posix))
21762306a36Sopenharmony_ci		return 0;
21862306a36Sopenharmony_ciagain:
21962306a36Sopenharmony_ci	file->f_locks = 0;
22062306a36Sopenharmony_ci	spin_lock(&flctx->flc_lock);
22162306a36Sopenharmony_ci	list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
22262306a36Sopenharmony_ci		if (fl->fl_lmops != &nlmsvc_lock_operations)
22362306a36Sopenharmony_ci			continue;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		/* update current lock count */
22662306a36Sopenharmony_ci		file->f_locks++;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
22962306a36Sopenharmony_ci		if (match(lockhost, host)) {
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			spin_unlock(&flctx->flc_lock);
23262306a36Sopenharmony_ci			if (nlm_unlock_files(file, fl))
23362306a36Sopenharmony_ci				return 1;
23462306a36Sopenharmony_ci			goto again;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	spin_unlock(&flctx->flc_lock);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int
24362306a36Sopenharmony_cinlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	return 1;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * Inspect a single file
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic inline int
25262306a36Sopenharmony_cinlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	nlmsvc_traverse_blocks(host, file, match);
25562306a36Sopenharmony_ci	nlmsvc_traverse_shares(host, file, match);
25662306a36Sopenharmony_ci	return nlm_traverse_locks(host, file, match);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * Quick check whether there are still any locks, blocks or
26162306a36Sopenharmony_ci * shares on a given file.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic inline int
26462306a36Sopenharmony_cinlm_file_inuse(struct nlm_file *file)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct inode	 *inode = nlmsvc_file_inode(file);
26762306a36Sopenharmony_ci	struct file_lock *fl;
26862306a36Sopenharmony_ci	struct file_lock_context *flctx = locks_inode_context(inode);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
27162306a36Sopenharmony_ci		return 1;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (flctx && !list_empty_careful(&flctx->flc_posix)) {
27462306a36Sopenharmony_ci		spin_lock(&flctx->flc_lock);
27562306a36Sopenharmony_ci		list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
27662306a36Sopenharmony_ci			if (fl->fl_lmops == &nlmsvc_lock_operations) {
27762306a36Sopenharmony_ci				spin_unlock(&flctx->flc_lock);
27862306a36Sopenharmony_ci				return 1;
27962306a36Sopenharmony_ci			}
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci		spin_unlock(&flctx->flc_lock);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	file->f_locks = 0;
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void nlm_close_files(struct nlm_file *file)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	if (file->f_file[O_RDONLY])
29062306a36Sopenharmony_ci		nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
29162306a36Sopenharmony_ci	if (file->f_file[O_WRONLY])
29262306a36Sopenharmony_ci		nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci * Loop over all files in the file table.
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_cistatic int
29962306a36Sopenharmony_cinlm_traverse_files(void *data, nlm_host_match_fn_t match,
30062306a36Sopenharmony_ci		int (*is_failover_file)(void *data, struct nlm_file *file))
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct hlist_node *next;
30362306a36Sopenharmony_ci	struct nlm_file	*file;
30462306a36Sopenharmony_ci	int i, ret = 0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	mutex_lock(&nlm_file_mutex);
30762306a36Sopenharmony_ci	for (i = 0; i < FILE_NRHASH; i++) {
30862306a36Sopenharmony_ci		hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
30962306a36Sopenharmony_ci			if (is_failover_file && !is_failover_file(data, file))
31062306a36Sopenharmony_ci				continue;
31162306a36Sopenharmony_ci			file->f_count++;
31262306a36Sopenharmony_ci			mutex_unlock(&nlm_file_mutex);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			/* Traverse locks, blocks and shares of this file
31562306a36Sopenharmony_ci			 * and update file->f_locks count */
31662306a36Sopenharmony_ci			if (nlm_inspect_file(data, file, match))
31762306a36Sopenharmony_ci				ret = 1;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci			mutex_lock(&nlm_file_mutex);
32062306a36Sopenharmony_ci			file->f_count--;
32162306a36Sopenharmony_ci			/* No more references to this file. Let go of it. */
32262306a36Sopenharmony_ci			if (list_empty(&file->f_blocks) && !file->f_locks
32362306a36Sopenharmony_ci			 && !file->f_shares && !file->f_count) {
32462306a36Sopenharmony_ci				hlist_del(&file->f_list);
32562306a36Sopenharmony_ci				nlm_close_files(file);
32662306a36Sopenharmony_ci				kfree(file);
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	mutex_unlock(&nlm_file_mutex);
33162306a36Sopenharmony_ci	return ret;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/*
33562306a36Sopenharmony_ci * Release file. If there are no more remote locks on this file,
33662306a36Sopenharmony_ci * close it and free the handle.
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * Note that we can't do proper reference counting without major
33962306a36Sopenharmony_ci * contortions because the code in fs/locks.c creates, deletes and
34062306a36Sopenharmony_ci * splits locks without notification. Our only way is to walk the
34162306a36Sopenharmony_ci * entire lock list each time we remove a lock.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_civoid
34462306a36Sopenharmony_cinlm_release_file(struct nlm_file *file)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
34762306a36Sopenharmony_ci				file, file->f_count);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Lock file table */
35062306a36Sopenharmony_ci	mutex_lock(&nlm_file_mutex);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* If there are no more locks etc, delete the file */
35362306a36Sopenharmony_ci	if (--file->f_count == 0 && !nlm_file_inuse(file))
35462306a36Sopenharmony_ci		nlm_delete_file(file);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mutex_unlock(&nlm_file_mutex);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * Helpers function for resource traversal
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * nlmsvc_mark_host:
36362306a36Sopenharmony_ci *	used by the garbage collector; simply sets h_inuse only for those
36462306a36Sopenharmony_ci *	hosts, which passed network check.
36562306a36Sopenharmony_ci *	Always returns 0.
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * nlmsvc_same_host:
36862306a36Sopenharmony_ci *	returns 1 iff the two hosts match. Used to release
36962306a36Sopenharmony_ci *	all resources bound to a specific host.
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * nlmsvc_is_client:
37262306a36Sopenharmony_ci *	returns 1 iff the host is a client.
37362306a36Sopenharmony_ci *	Used by nlmsvc_invalidate_all
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int
37762306a36Sopenharmony_cinlmsvc_mark_host(void *data, struct nlm_host *hint)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct nlm_host *host = data;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if ((hint->net == NULL) ||
38262306a36Sopenharmony_ci	    (host->net == hint->net))
38362306a36Sopenharmony_ci		host->h_inuse = 1;
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int
38862306a36Sopenharmony_cinlmsvc_same_host(void *data, struct nlm_host *other)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct nlm_host *host = data;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return host == other;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int
39662306a36Sopenharmony_cinlmsvc_is_client(void *data, struct nlm_host *dummy)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct nlm_host *host = data;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (host->h_server) {
40162306a36Sopenharmony_ci		/* we are destroying locks even though the client
40262306a36Sopenharmony_ci		 * hasn't asked us too, so don't unmonitor the
40362306a36Sopenharmony_ci		 * client
40462306a36Sopenharmony_ci		 */
40562306a36Sopenharmony_ci		if (host->h_nsmhandle)
40662306a36Sopenharmony_ci			host->h_nsmhandle->sm_sticky = 1;
40762306a36Sopenharmony_ci		return 1;
40862306a36Sopenharmony_ci	} else
40962306a36Sopenharmony_ci		return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/*
41362306a36Sopenharmony_ci * Mark all hosts that still hold resources
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_civoid
41662306a36Sopenharmony_cinlmsvc_mark_resources(struct net *net)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct nlm_host hint;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
42162306a36Sopenharmony_ci	hint.net = net;
42262306a36Sopenharmony_ci	nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/*
42662306a36Sopenharmony_ci * Release all resources held by the given client
42762306a36Sopenharmony_ci */
42862306a36Sopenharmony_civoid
42962306a36Sopenharmony_cinlmsvc_free_host_resources(struct nlm_host *host)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_free_host_resources\n");
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
43462306a36Sopenharmony_ci		printk(KERN_WARNING
43562306a36Sopenharmony_ci			"lockd: couldn't remove all locks held by %s\n",
43662306a36Sopenharmony_ci			host->h_name);
43762306a36Sopenharmony_ci		BUG();
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * nlmsvc_invalidate_all - remove all locks held for clients
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * Release all locks held by NFS clients.
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_civoid
44862306a36Sopenharmony_cinlmsvc_invalidate_all(void)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	/*
45162306a36Sopenharmony_ci	 * Previously, the code would call
45262306a36Sopenharmony_ci	 * nlmsvc_free_host_resources for each client in
45362306a36Sopenharmony_ci	 * turn, which is about as inefficient as it gets.
45462306a36Sopenharmony_ci	 * Now we just do it once in nlm_traverse_files.
45562306a36Sopenharmony_ci	 */
45662306a36Sopenharmony_ci	nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int
46162306a36Sopenharmony_cinlmsvc_match_sb(void *datap, struct nlm_file *file)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct super_block *sb = datap;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return sb == nlmsvc_file_inode(file)->i_sb;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/**
46962306a36Sopenharmony_ci * nlmsvc_unlock_all_by_sb - release locks held on this file system
47062306a36Sopenharmony_ci * @sb: super block
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * Release all locks held by clients accessing this file system.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_ciint
47562306a36Sopenharmony_cinlmsvc_unlock_all_by_sb(struct super_block *sb)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	int ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
48062306a36Sopenharmony_ci	return ret ? -EIO : 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int
48562306a36Sopenharmony_cinlmsvc_match_ip(void *datap, struct nlm_host *host)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	return rpc_cmp_addr(nlm_srcaddr(host), datap);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/**
49162306a36Sopenharmony_ci * nlmsvc_unlock_all_by_ip - release local locks by IP address
49262306a36Sopenharmony_ci * @server_addr: server's IP address as seen by clients
49362306a36Sopenharmony_ci *
49462306a36Sopenharmony_ci * Release all locks held by clients accessing this host
49562306a36Sopenharmony_ci * via the passed in IP address.
49662306a36Sopenharmony_ci */
49762306a36Sopenharmony_ciint
49862306a36Sopenharmony_cinlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	int ret;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
50362306a36Sopenharmony_ci	return ret ? -EIO : 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);
506