linux/fs/lockd/svcsubs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/fs/lockd/svcsubs.c
   4 *
   5 * Various support routines for the NLM server.
   6 *
   7 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
   8 */
   9
  10#include <linux/types.h>
  11#include <linux/string.h>
  12#include <linux/time.h>
  13#include <linux/in.h>
  14#include <linux/slab.h>
  15#include <linux/mutex.h>
  16#include <linux/sunrpc/svc.h>
  17#include <linux/sunrpc/addr.h>
  18#include <linux/lockd/lockd.h>
  19#include <linux/lockd/share.h>
  20#include <linux/module.h>
  21#include <linux/mount.h>
  22#include <uapi/linux/nfs2.h>
  23
  24#define NLMDBG_FACILITY         NLMDBG_SVCSUBS
  25
  26
  27/*
  28 * Global file hash table
  29 */
  30#define FILE_HASH_BITS          7
  31#define FILE_NRHASH             (1<<FILE_HASH_BITS)
  32static struct hlist_head        nlm_files[FILE_NRHASH];
  33static DEFINE_MUTEX(nlm_file_mutex);
  34
  35#ifdef CONFIG_SUNRPC_DEBUG
  36static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
  37{
  38        u32 *fhp = (u32*)f->data;
  39
  40        /* print the first 32 bytes of the fh */
  41        dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
  42                msg, fhp[0], fhp[1], fhp[2], fhp[3],
  43                fhp[4], fhp[5], fhp[6], fhp[7]);
  44}
  45
  46static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
  47{
  48        struct inode *inode = nlmsvc_file_inode(file);
  49
  50        dprintk("lockd: %s %s/%ld\n",
  51                msg, inode->i_sb->s_id, inode->i_ino);
  52}
  53#else
  54static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
  55{
  56        return;
  57}
  58
  59static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
  60{
  61        return;
  62}
  63#endif
  64
  65static inline unsigned int file_hash(struct nfs_fh *f)
  66{
  67        unsigned int tmp=0;
  68        int i;
  69        for (i=0; i<NFS2_FHSIZE;i++)
  70                tmp += f->data[i];
  71        return tmp & (FILE_NRHASH - 1);
  72}
  73
  74int lock_to_openmode(struct file_lock *lock)
  75{
  76        return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
  77}
  78
  79/*
  80 * Open the file. Note that if we're reexporting, for example,
  81 * this could block the lockd thread for a while.
  82 *
  83 * We have to make sure we have the right credential to open
  84 * the file.
  85 */
  86static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
  87                           struct nlm_file *file, int mode)
  88{
  89        struct file **fp = &file->f_file[mode];
  90        __be32  nfserr;
  91
  92        if (*fp)
  93                return 0;
  94        nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
  95        if (nfserr)
  96                dprintk("lockd: open failed (error %d)\n", nfserr);
  97        return nfserr;
  98}
  99
 100/*
 101 * Lookup file info. If it doesn't exist, create a file info struct
 102 * and open a (VFS) file for the given inode.
 103 */
 104__be32
 105nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 106                                        struct nlm_lock *lock)
 107{
 108        struct nlm_file *file;
 109        unsigned int    hash;
 110        __be32          nfserr;
 111        int             mode;
 112
 113        nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
 114
 115        hash = file_hash(&lock->fh);
 116        mode = lock_to_openmode(&lock->fl);
 117
 118        /* Lock file table */
 119        mutex_lock(&nlm_file_mutex);
 120
 121        hlist_for_each_entry(file, &nlm_files[hash], f_list)
 122                if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
 123                        mutex_lock(&file->f_mutex);
 124                        nfserr = nlm_do_fopen(rqstp, file, mode);
 125                        mutex_unlock(&file->f_mutex);
 126                        goto found;
 127                }
 128        nlm_debug_print_fh("creating file for", &lock->fh);
 129
 130        nfserr = nlm_lck_denied_nolocks;
 131        file = kzalloc(sizeof(*file), GFP_KERNEL);
 132        if (!file)
 133                goto out_free;
 134
 135        memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
 136        mutex_init(&file->f_mutex);
 137        INIT_HLIST_NODE(&file->f_list);
 138        INIT_LIST_HEAD(&file->f_blocks);
 139
 140        nfserr = nlm_do_fopen(rqstp, file, mode);
 141        if (nfserr)
 142                goto out_unlock;
 143
 144        hlist_add_head(&file->f_list, &nlm_files[hash]);
 145
 146found:
 147        dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
 148        *result = file;
 149        file->f_count++;
 150
 151out_unlock:
 152        mutex_unlock(&nlm_file_mutex);
 153        return nfserr;
 154
 155out_free:
 156        kfree(file);
 157        goto out_unlock;
 158}
 159
 160/*
 161 * Delete a file after having released all locks, blocks and shares
 162 */
 163static inline void
 164nlm_delete_file(struct nlm_file *file)
 165{
 166        nlm_debug_print_file("closing file", file);
 167        if (!hlist_unhashed(&file->f_list)) {
 168                hlist_del(&file->f_list);
 169                if (file->f_file[O_RDONLY])
 170                        nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
 171                if (file->f_file[O_WRONLY])
 172                        nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
 173                kfree(file);
 174        } else {
 175                printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
 176        }
 177}
 178
 179static int nlm_unlock_files(struct nlm_file *file)
 180{
 181        struct file_lock lock;
 182
 183        locks_init_lock(&lock);
 184        lock.fl_type  = F_UNLCK;
 185        lock.fl_start = 0;
 186        lock.fl_end   = OFFSET_MAX;
 187        if (file->f_file[O_RDONLY] &&
 188            vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, &lock, NULL))
 189                goto out_err;
 190        if (file->f_file[O_WRONLY] &&
 191            vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, &lock, NULL))
 192                goto out_err;
 193        return 0;
 194out_err:
 195        pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
 196        return 1;
 197}
 198
 199/*
 200 * Loop over all locks on the given file and perform the specified
 201 * action.
 202 */
 203static int
 204nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
 205                        nlm_host_match_fn_t match)
 206{
 207        struct inode     *inode = nlmsvc_file_inode(file);
 208        struct file_lock *fl;
 209        struct file_lock_context *flctx = inode->i_flctx;
 210        struct nlm_host  *lockhost;
 211
 212        if (!flctx || list_empty_careful(&flctx->flc_posix))
 213                return 0;
 214again:
 215        file->f_locks = 0;
 216        spin_lock(&flctx->flc_lock);
 217        list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
 218                if (fl->fl_lmops != &nlmsvc_lock_operations)
 219                        continue;
 220
 221                /* update current lock count */
 222                file->f_locks++;
 223
 224                lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
 225                if (match(lockhost, host)) {
 226
 227                        spin_unlock(&flctx->flc_lock);
 228                        if (nlm_unlock_files(file))
 229                                return 1;
 230                        goto again;
 231                }
 232        }
 233        spin_unlock(&flctx->flc_lock);
 234
 235        return 0;
 236}
 237
 238static int
 239nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
 240{
 241        return 1;
 242}
 243
 244/*
 245 * Inspect a single file
 246 */
 247static inline int
 248nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
 249{
 250        nlmsvc_traverse_blocks(host, file, match);
 251        nlmsvc_traverse_shares(host, file, match);
 252        return nlm_traverse_locks(host, file, match);
 253}
 254
 255/*
 256 * Quick check whether there are still any locks, blocks or
 257 * shares on a given file.
 258 */
 259static inline int
 260nlm_file_inuse(struct nlm_file *file)
 261{
 262        struct inode     *inode = nlmsvc_file_inode(file);
 263        struct file_lock *fl;
 264        struct file_lock_context *flctx = inode->i_flctx;
 265
 266        if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
 267                return 1;
 268
 269        if (flctx && !list_empty_careful(&flctx->flc_posix)) {
 270                spin_lock(&flctx->flc_lock);
 271                list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
 272                        if (fl->fl_lmops == &nlmsvc_lock_operations) {
 273                                spin_unlock(&flctx->flc_lock);
 274                                return 1;
 275                        }
 276                }
 277                spin_unlock(&flctx->flc_lock);
 278        }
 279        file->f_locks = 0;
 280        return 0;
 281}
 282
 283static void nlm_close_files(struct nlm_file *file)
 284{
 285        struct file *f;
 286
 287        for (f = file->f_file[0]; f <= file->f_file[1]; f++)
 288                if (f)
 289                        nlmsvc_ops->fclose(f);
 290}
 291
 292/*
 293 * Loop over all files in the file table.
 294 */
 295static int
 296nlm_traverse_files(void *data, nlm_host_match_fn_t match,
 297                int (*is_failover_file)(void *data, struct nlm_file *file))
 298{
 299        struct hlist_node *next;
 300        struct nlm_file *file;
 301        int i, ret = 0;
 302
 303        mutex_lock(&nlm_file_mutex);
 304        for (i = 0; i < FILE_NRHASH; i++) {
 305                hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
 306                        if (is_failover_file && !is_failover_file(data, file))
 307                                continue;
 308                        file->f_count++;
 309                        mutex_unlock(&nlm_file_mutex);
 310
 311                        /* Traverse locks, blocks and shares of this file
 312                         * and update file->f_locks count */
 313                        if (nlm_inspect_file(data, file, match))
 314                                ret = 1;
 315
 316                        mutex_lock(&nlm_file_mutex);
 317                        file->f_count--;
 318                        /* No more references to this file. Let go of it. */
 319                        if (list_empty(&file->f_blocks) && !file->f_locks
 320                         && !file->f_shares && !file->f_count) {
 321                                hlist_del(&file->f_list);
 322                                nlm_close_files(file);
 323                                kfree(file);
 324                        }
 325                }
 326        }
 327        mutex_unlock(&nlm_file_mutex);
 328        return ret;
 329}
 330
 331/*
 332 * Release file. If there are no more remote locks on this file,
 333 * close it and free the handle.
 334 *
 335 * Note that we can't do proper reference counting without major
 336 * contortions because the code in fs/locks.c creates, deletes and
 337 * splits locks without notification. Our only way is to walk the
 338 * entire lock list each time we remove a lock.
 339 */
 340void
 341nlm_release_file(struct nlm_file *file)
 342{
 343        dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
 344                                file, file->f_count);
 345
 346        /* Lock file table */
 347        mutex_lock(&nlm_file_mutex);
 348
 349        /* If there are no more locks etc, delete the file */
 350        if (--file->f_count == 0 && !nlm_file_inuse(file))
 351                nlm_delete_file(file);
 352
 353        mutex_unlock(&nlm_file_mutex);
 354}
 355
 356/*
 357 * Helpers function for resource traversal
 358 *
 359 * nlmsvc_mark_host:
 360 *      used by the garbage collector; simply sets h_inuse only for those
 361 *      hosts, which passed network check.
 362 *      Always returns 0.
 363 *
 364 * nlmsvc_same_host:
 365 *      returns 1 iff the two hosts match. Used to release
 366 *      all resources bound to a specific host.
 367 *
 368 * nlmsvc_is_client:
 369 *      returns 1 iff the host is a client.
 370 *      Used by nlmsvc_invalidate_all
 371 */
 372
 373static int
 374nlmsvc_mark_host(void *data, struct nlm_host *hint)
 375{
 376        struct nlm_host *host = data;
 377
 378        if ((hint->net == NULL) ||
 379            (host->net == hint->net))
 380                host->h_inuse = 1;
 381        return 0;
 382}
 383
 384static int
 385nlmsvc_same_host(void *data, struct nlm_host *other)
 386{
 387        struct nlm_host *host = data;
 388
 389        return host == other;
 390}
 391
 392static int
 393nlmsvc_is_client(void *data, struct nlm_host *dummy)
 394{
 395        struct nlm_host *host = data;
 396
 397        if (host->h_server) {
 398                /* we are destroying locks even though the client
 399                 * hasn't asked us too, so don't unmonitor the
 400                 * client
 401                 */
 402                if (host->h_nsmhandle)
 403                        host->h_nsmhandle->sm_sticky = 1;
 404                return 1;
 405        } else
 406                return 0;
 407}
 408
 409/*
 410 * Mark all hosts that still hold resources
 411 */
 412void
 413nlmsvc_mark_resources(struct net *net)
 414{
 415        struct nlm_host hint;
 416
 417        dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
 418        hint.net = net;
 419        nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
 420}
 421
 422/*
 423 * Release all resources held by the given client
 424 */
 425void
 426nlmsvc_free_host_resources(struct nlm_host *host)
 427{
 428        dprintk("lockd: nlmsvc_free_host_resources\n");
 429
 430        if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
 431                printk(KERN_WARNING
 432                        "lockd: couldn't remove all locks held by %s\n",
 433                        host->h_name);
 434                BUG();
 435        }
 436}
 437
 438/**
 439 * nlmsvc_invalidate_all - remove all locks held for clients
 440 *
 441 * Release all locks held by NFS clients.
 442 *
 443 */
 444void
 445nlmsvc_invalidate_all(void)
 446{
 447        /*
 448         * Previously, the code would call
 449         * nlmsvc_free_host_resources for each client in
 450         * turn, which is about as inefficient as it gets.
 451         * Now we just do it once in nlm_traverse_files.
 452         */
 453        nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
 454}
 455
 456
 457static int
 458nlmsvc_match_sb(void *datap, struct nlm_file *file)
 459{
 460        struct super_block *sb = datap;
 461
 462        return sb == nlmsvc_file_inode(file)->i_sb;
 463}
 464
 465/**
 466 * nlmsvc_unlock_all_by_sb - release locks held on this file system
 467 * @sb: super block
 468 *
 469 * Release all locks held by clients accessing this file system.
 470 */
 471int
 472nlmsvc_unlock_all_by_sb(struct super_block *sb)
 473{
 474        int ret;
 475
 476        ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
 477        return ret ? -EIO : 0;
 478}
 479EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
 480
 481static int
 482nlmsvc_match_ip(void *datap, struct nlm_host *host)
 483{
 484        return rpc_cmp_addr(nlm_srcaddr(host), datap);
 485}
 486
 487/**
 488 * nlmsvc_unlock_all_by_ip - release local locks by IP address
 489 * @server_addr: server's IP address as seen by clients
 490 *
 491 * Release all locks held by clients accessing this host
 492 * via the passed in IP address.
 493 */
 494int
 495nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
 496{
 497        int ret;
 498
 499        ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
 500        return ret ? -EIO : 0;
 501}
 502EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);
 503