Whamcloud - gitweb
b=21122 fix a race between page fault and lock cancel.
[fs/lustre-release.git] / lustre / llite / llite_mmap.c
index ae43a07..c955661 100644 (file)
@@ -69,9 +69,6 @@
                vma->vm_file->f_dentry->d_inode->i_ino,                       \
                vma->vm_file->f_dentry->d_iname, ## arg);                     \
 
-struct page *ll_nopage(struct vm_area_struct *vma, unsigned long address,
-                       int *type);
-
 static struct vm_operations_struct ll_file_vm_ops;
 
 void policy_from_vma(ldlm_policy_data_t *policy,
@@ -206,8 +203,8 @@ struct page *ll_nopage(struct vm_area_struct *vma, unsigned long address,
         struct lu_env           *env;
         struct cl_env_nest      nest;
         struct cl_io            *io;
-        struct page             *page  = NOPAGE_SIGBUS;
-        struct vvp_io           *vio = NULL;
+        struct page             *page;
+        struct vvp_io           *vio;
         unsigned long           ra_flags;
         pgoff_t                 pg_offset;
         int                     result;
@@ -223,22 +220,27 @@ struct page *ll_nopage(struct vm_area_struct *vma, unsigned long address,
                 goto out_err;
 
         vio = vvp_env_io(env);
-
         vio->u.fault.ft_vma            = vma;
+        vio->u.fault.ft_vmpage         = NULL;
         vio->u.fault.nopage.ft_address = address;
         vio->u.fault.nopage.ft_type    = type;
 
         result = cl_io_loop(env, io);
 
-out_err:
-        if (result == 0) {
-                LASSERT(io->u.ci_fault.ft_page != NULL);
-                page = vio->u.fault.ft_vmpage;
-        } else {
-                if (result == -ENOMEM)
-                        page = NOPAGE_OOM;
+        page = vio->u.fault.ft_vmpage;
+        if (page != NULL) {
+                LASSERT(PageLocked(page));
+                unlock_page(page);
+
+                if (result != 0)
+                        page_cache_release(page);
         }
 
+        LASSERT(ergo(result == 0, io->u.ci_fault.ft_page != NULL));
+out_err:
+        if (result != 0)
+                page = result == -ENOMEM ? NOPAGE_OOM : NOPAGE_SIGBUS;
+
         vma->vm_flags &= ~VM_RAND_READ;
         vma->vm_flags |= ra_flags;
 
@@ -263,7 +265,7 @@ int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
         struct lu_env           *env;
         struct cl_io            *io;
-        struct vvp_io           *vio = NULL;
+        struct vvp_io           *vio;
         unsigned long            ra_flags;
         struct cl_env_nest       nest;
         int                      result;
@@ -279,13 +281,22 @@ int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                 goto out_err;
 
         vio = vvp_env_io(env);
-
         vio->u.fault.ft_vma       = vma;
+        vio->u.fault.ft_vmpage    = NULL;
         vio->u.fault.fault.ft_vmf = vmf;
 
         result = cl_io_loop(env, io);
-        fault_ret = vio->u.fault.fault.ft_flags;
+        if (unlikely(result != 0 && vio->u.fault.ft_vmpage != NULL)) {
+                struct page *vmpage = vio->u.fault.ft_vmpage;
+
+                LASSERT((vio->u.fault.fault.ft_flags & VM_FAULT_LOCKED) &&
+                        PageLocked(vmpage));
+                unlock_page(vmpage);
+                page_cache_release(vmpage);
+                vmf->page = NULL;
+        }
 
+        fault_ret = vio->u.fault.fault.ft_flags;
 out_err:
         if (result != 0)
                 fault_ret |= VM_FAULT_ERROR;