LCOV - code coverage report
Current view: top level - fs/lockd - svcsubs.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 105 0.0 %
Date: 2014-02-18 Functions: 0 15 0.0 %
Branches: 0 74 0.0 %

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

Generated by: LCOV version 1.9