misc: tegra-throughput: prevent race on init
[linux-2.6.git] / drivers / misc / tegra-throughput.c
index ec36176..47dc682 100644 (file)
@@ -25,8 +25,8 @@
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/throughput_ioctl.h>
-#include <linux/nvhost.h>
 #include <linux/module.h>
+#include <linux/nvhost.h>
 #include <mach/dc.h>
 
 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
@@ -58,6 +58,9 @@ static int throughput_flip_notifier(struct notifier_block *nb,
                        else
                                last_frame_time = (unsigned short) timediff;
 
+                       if (last_frame_time == 0)
+                               return NOTIFY_DONE;
+
                        throughput_hint =
                                ((int) target_frame_time * 100)/last_frame_time;
 
@@ -96,19 +99,24 @@ static int notifier_initialized;
 
 static int throughput_open(struct inode *inode, struct file *file)
 {
+       int need_init = 0;
+
+       spin_lock(&lock);
+
        if (!notifier_initialized) {
-               tegra_dc_register_flip_notifier(&throughput_flip_nb);
                notifier_initialized = 1;
+               need_init = 1;
        }
 
-       spin_lock(&lock);
-
        throughput_active_app_count++;
        if (throughput_active_app_count > 1)
                multiple_app_disable = 1;
 
        spin_unlock(&lock);
 
+       if (need_init)
+               tegra_dc_register_flip_notifier(&throughput_flip_nb);
+
        pr_debug("throughput_open node %p file %p\n", inode, file);
 
        return 0;
@@ -116,17 +124,22 @@ static int throughput_open(struct inode *inode, struct file *file)
 
 static int throughput_release(struct inode *inode, struct file *file)
 {
+       int need_deinit = 0;
+
        spin_lock(&lock);
-       throughput_active_app_count--;
-       spin_unlock(&lock);
 
+       throughput_active_app_count--;
        if (throughput_active_app_count == 0) {
                reset_target_frame_time();
                multiple_app_disable = 0;
-               tegra_dc_unregister_flip_notifier(&throughput_flip_nb);
                notifier_initialized = 0;
+               need_deinit = 1;
        }
 
+       spin_unlock(&lock);
+
+       if (need_deinit)
+               tegra_dc_unregister_flip_notifier(&throughput_flip_nb);
 
        pr_debug("throughput_release node %p file %p\n", inode, file);