gru: handle failures to mmu_notifier_register
authorJack Steiner <steiner@sgi.com>
Wed, 16 Dec 2009 00:48:08 +0000 (16:48 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Dec 2009 15:20:15 +0000 (07:20 -0800)
Under some conditions, mmu_notifier_register() will fail to register a
mmu_notifier.  Fix the GRU driver to correctly handle these failures.

Signed-off-by: Jack Steiner <steiner@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/misc/sgi-gru/grufault.c
drivers/misc/sgi-gru/grumain.c
drivers/misc/sgi-gru/grutlbpurge.c

index 9470303..ada7df7 100644 (file)
@@ -96,7 +96,7 @@ static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr)
        vma = gru_find_vma(vaddr);
        if (vma)
                gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
-       if (gts) {
+       if (!IS_ERR(gts)) {
                mutex_lock(&gts->ts_ctxlock);
                downgrade_write(&mm->mmap_sem);
        } else {
@@ -747,8 +747,8 @@ int gru_set_context_option(unsigned long arg)
        gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1);
 
        gts = gru_alloc_locked_gts(req.gseg);
-       if (!gts)
-               return -EINVAL;
+       if (IS_ERR(gts))
+               return PTR_ERR(gts);
 
        switch (req.op) {
        case sco_blade_chiplet:
index 9ec54bd..9440288 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/sched.h>
 #include <linux/device.h>
 #include <linux/list.h>
+#include <linux/err.h>
 #include <asm/uv/uv_hub.h>
 #include "gru.h"
 #include "grutables.h"
@@ -286,7 +287,8 @@ static void gru_unload_mm_tracker(struct gru_state *gru,
 void gts_drop(struct gru_thread_state *gts)
 {
        if (gts && atomic_dec_return(&gts->ts_refcnt) == 0) {
-               gru_drop_mmu_notifier(gts->ts_gms);
+               if (gts->ts_gms)
+                       gru_drop_mmu_notifier(gts->ts_gms);
                kfree(gts);
                STAT(gts_free);
        }
@@ -313,13 +315,14 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
                int cbr_au_count, int dsr_au_count, int options, int tsid)
 {
        struct gru_thread_state *gts;
+       struct gru_mm_struct *gms;
        int bytes;
 
        bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count);
        bytes += sizeof(struct gru_thread_state);
        gts = kmalloc(bytes, GFP_KERNEL);
        if (!gts)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        STAT(gts_alloc);
        memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */
@@ -338,9 +341,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
        if (vma) {
                gts->ts_mm = current->mm;
                gts->ts_vma = vma;
-               gts->ts_gms = gru_register_mmu_notifier();
-               if (!gts->ts_gms)
+               gms = gru_register_mmu_notifier();
+               if (IS_ERR(gms))
                        goto err;
+               gts->ts_gms = gms;
        }
 
        gru_dbg(grudev, "alloc gts %p\n", gts);
@@ -348,7 +352,7 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
 
 err:
        gts_drop(gts);
-       return NULL;
+       return ERR_CAST(gms);
 }
 
 /*
@@ -396,8 +400,8 @@ struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma,
 
        gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, vdata->vd_dsr_au_count,
                            vdata->vd_user_options, tsid);
-       if (!gts)
-               return NULL;
+       if (IS_ERR(gts))
+               return gts;
 
        spin_lock(&vdata->vd_lock);
        ngts = gru_find_current_gts_nolock(vdata, tsid);
index 1d12509..421c548 100644 (file)
@@ -299,6 +299,7 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
 {
        struct gru_mm_struct *gms;
        struct mmu_notifier *mn;
+       int err;
 
        mn = mmu_find_ops(current->mm, &gru_mmuops);
        if (mn) {
@@ -311,12 +312,17 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
                        gms->ms_notifier.ops = &gru_mmuops;
                        atomic_set(&gms->ms_refcnt, 1);
                        init_waitqueue_head(&gms->ms_wait_queue);
-                       __mmu_notifier_register(&gms->ms_notifier, current->mm);
+                       err = __mmu_notifier_register(&gms->ms_notifier, current->mm);
+                       if (err)
+                               goto error;
                }
        }
        gru_dbg(grudev, "gms %p, refcnt %d\n", gms,
                atomic_read(&gms->ms_refcnt));
        return gms;
+error:
+       kfree(gms);
+       return ERR_PTR(err);
 }
 
 void gru_drop_mmu_notifier(struct gru_mm_struct *gms)