video: tegra: nvmap: Several changes to carveout killer
authorRebecca Schultz Zavin <rebecca@android.com>
Mon, 24 Jan 2011 22:16:47 +0000 (14:16 -0800)
committerRebecca Schultz Zavin <rebecca@android.com>
Mon, 24 Jan 2011 23:44:07 +0000 (15:44 -0800)
-Add a module param to enable/disable carveout killer
-Fix race condition in code to wait for something to free memory
after firing carveout killer
-Fix the check for current so we always compare task->group_leaders

Change-Id: Ie030978827dce6b0fbbfa1db0d80e4abe59eaa51
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
drivers/video/tegra/nvmap/nvmap_dev.c

index 674b34ab6f45e2d07a351df7c1359d7ef587dc04..2cea073499b7f08cda84462a3ceebef1909c98e4 100644 (file)
 #define NVMAP_NUM_PTES         64
 #define NVMAP_CARVEOUT_KILLER_RETRY_TIME 100 /* msecs */
 
+#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
+static bool carveout_killer = true;
+#else
+static bool carveout_killer;
+#endif
+module_param(carveout_killer, bool, 0640);
+
 struct nvmap_carveout_node {
        unsigned int            heap_bit;
        struct nvmap_heap       *carveout;
@@ -324,8 +331,8 @@ static struct nvmap_client* get_client_from_carveout_commit(
                                               carveout_commit);
 }
 
-#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
 static DECLARE_WAIT_QUEUE_HEAD(wait_reclaim);
+static int wait_count;
 bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
 {
        struct nvmap_carveout_commit *commit;
@@ -359,6 +366,9 @@ bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
                sig = task->signal;
                if (!task->mm || !sig)
                        goto end;
+               /* don't try to kill current */
+               if (task == current->group_leader)
+                       goto end;
                /* don't try to kill higher priority tasks */
                if (sig->oom_adj < current_oom_adj)
                        goto end;
@@ -374,22 +384,22 @@ end:
                task_unlock(task);
        }
        if (selected_task) {
-               wait = selected_task != current;
+               wait = true;
                if (fatal_signal_pending(selected_task)) {
                        pr_warning("carveout_killer: process %d dying "
                                   "slowly\n", selected_task->pid);
                        goto out;
                }
                pr_info("carveout_killer: killing process %d with oom_adj %d "
-                       "to reclaim %d\n", selected_task->pid, selected_oom_adj,
-                       selected_size);
+                       "to reclaim %d (for process with oom_adj %d)\n",
+                       selected_task->pid, selected_oom_adj,
+                       selected_size, current_oom_adj);
                force_sig(SIGKILL, selected_task);
        }
 out:
        spin_unlock_irqrestore(&node->clients_lock, flags);
        return wait;
 }
-#endif
 
 struct nvmap_heap_block *do_nvmap_carveout_alloc(struct nvmap_client *client,
                                                 size_t len, size_t align,
@@ -422,63 +432,75 @@ struct nvmap_heap_block *do_nvmap_carveout_alloc(struct nvmap_client *client,
        return NULL;
 }
 
+static bool nvmap_carveout_freed(int count)
+{
+       smp_rmb();
+       return count != wait_count;
+}
+
 struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
                                              size_t len, size_t align,
                                              unsigned long usage,
                                              unsigned int prot)
 {
        struct nvmap_heap_block *block;
-#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
        struct nvmap_carveout_node *co_heap;
        struct nvmap_device *dev = client->dev;
        int i;
        unsigned long end = jiffies +
                msecs_to_jiffies(NVMAP_CARVEOUT_KILLER_RETRY_TIME);
        int count = 0;
-       DEFINE_WAIT(wait);
 
        do {
-               block = do_nvmap_carveout_alloc(client, len, align, usage,
-                                               prot);
+               block = do_nvmap_carveout_alloc(client, len, align,
+                                               usage, prot);
+               if (!carveout_killer)
+                       return block;
+
                if (block)
                        return block;
 
-               if (!count++)
-                       printk("%s: failed to allocate %u bytes, "
-                              "firing carveout killer!\n", __func__, len);
-               else
-                       printk("%s: still can't allocate %u bytes, "
-                              "attempt %d!\n", __func__, len, count);
+               if (!count++) {
+                       char task_comm[TASK_COMM_LEN];
+                       if (client->task)
+                               get_task_comm(task_comm, client->task);
+                       else
+                               task_comm[0] = 0;
+                       pr_info("%s: failed to allocate %u bytes for "
+                               "process %s, firing carveout "
+                               "killer!\n", __func__, len, task_comm);
+
+               } else {
+                       pr_info("%s: still can't allocate %u bytes, "
+                               "attempt %d!\n", __func__, len, count);
+               }
 
                /* shrink carveouts that matter and try again */
                for (i = 0; i < dev->nr_carveouts; i++) {
+                       int count;
                        co_heap = &dev->heaps[i];
 
                        if (!(co_heap->heap_bit & usage))
                                continue;
 
-                       /* indicates we just delivered a sigkill to current,
-                          or didn't find anything to kill might as well stop
-                          trying */
+                       count = wait_count;
+                       /* indicates we didn't find anything to kill,
+                          might as well stop trying */
                        if (!nvmap_shrink_carveout(co_heap))
                                return NULL;
 
-                       prepare_to_wait(&wait_reclaim, &wait,
-                                       TASK_INTERRUPTIBLE);
-                       schedule_timeout(end - jiffies);
-                       finish_wait(&wait_reclaim, &wait);
+                       if (time_is_after_jiffies(end))
+                               wait_event_interruptible_timeout(wait_reclaim,
+                                        nvmap_carveout_freed(count),
+                                        end - jiffies);
                }
        } while (time_is_after_jiffies(end));
 
        if (time_is_before_jiffies(end))
-           printk("carveout_killer: timeout expired without allocation "
-                  "succeeding.\n");
+               pr_info("carveout_killer: timeout expired without "
+                       "allocation succeeding.\n");
 
        return NULL;
-#else
-       block = do_nvmap_carveout_alloc(client, len, align, usage, prot);
-       return block;
-#endif
 }
 
 /* remove a handle from the device's tree of all handles; called
@@ -588,17 +610,17 @@ struct nvmap_client *nvmap_create_client(struct nvmap_device *dev,
                client->carveout_commit[i].commit = 0;
        }
 
-       get_task_struct(current);
-       task_lock(current);
+       get_task_struct(current->group_leader);
+       task_lock(current->group_leader);
        /* don't bother to store task struct for kernel threads,
           they can't be killed anyway */
        if (current->flags & PF_KTHREAD) {
-               put_task_struct(current);
+               put_task_struct(current->group_leader);
                task = NULL;
        } else {
-               task = current;
+               task = current->group_leader;
        }
-       task_unlock(current);
+       task_unlock(current->group_leader);
        client->task = task;
 
        spin_lock_init(&client->ref_lock);
@@ -641,9 +663,11 @@ static void destroy_client(struct nvmap_client *client)
                kfree(ref);
        }
 
-#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
-       wake_up_all(&wait_reclaim);
-#endif
+       if (carveout_killer) {
+               wait_count++;
+               smp_wmb();
+               wake_up_all(&wait_reclaim);
+       }
 
        for (i = 0; i < client->dev->nr_carveouts; i++)
                list_del(&client->carveout_commit[i].list);