• Frank Filz's avatar
    NFSv4: Make sure unlock is really an unlock when cancelling a lock · 137d6aca
    Frank Filz authored
    I ran into a curious issue when a lock is being canceled. The
    cancellation results in a lock request to the vfs layer instead of an
    unlock request. This is particularly insidious when the process that
    owns the lock is exiting. In that case, sometimes the erroneous lock is
    applied AFTER the process has entered zombie state, preventing the lock
    from ever being released. Eventually other processes block on the lock
    causing a slow degredation of the system. In the 2.6.16 kernel this was
    investigated on, the problem is compounded by the fact that the cl_sem
    is held while blocking on the vfs lock, which results in most processes
    accessing the nfs file system in question hanging.
    
    In more detail, here is how the situation occurs:
    
    first _nfs4_do_setlk():
    
    static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim)
    ...
            ret = nfs4_wait_for_completion_rpc_task(task);
            if (ret == 0) {
    ...
            } else
                    data->cancelled = 1;
    
    then nfs4_lock_release():
    
    static void nfs4_lock_release(void *calldata)
    ...
            if (data->cancelled != 0) {
                    struct rpc_task *task;
                    task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
                                    data->arg.lock_seqid);
    
    The problem is the same file_lock that was passed in to _nfs4_do_setlk()
    gets passed to nfs4_do_unlck() from nfs4_lock_release(). So the type is
    still F_RDLCK or FWRLCK, not F_UNLCK. At some point, when cancelling the
    lock, the type needs to be changed to F_UNLCK. It seemed easiest to do
    that in nfs4_do_unlck(), but it could be done in nfs4_lock_release().
    The concern I had with doing it there was if something still needed the
    original file_lock, though it turns out the original file_lock still
    needs to be modified by nfs4_do_unlck() because nfs4_do_unlck() uses the
    original file_lock to pass to the vfs layer, and a copy of the original
    file_lock for the RPC request.
    
    It seems like the simplest solution is to force all situations where
    nfs4_do_unlck() is being used to result in an unlock, so with that in
    mind, I made the following change:
    Signed-off-by: default avatarFrank Filz <ffilzlnx@us.ibm.com>
    Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
    137d6aca
nfs4proc.c 100 KB