video: tegra: nvmap: Several fixes to the carveout killer
authorRebecca Schultz Zavin <rebecca@android.com>
Thu, 13 Jan 2011 00:22:36 +0000 (16:22 -0800)
committerRebecca Schultz Zavin <rebecca@android.com>
Thu, 13 Jan 2011 01:37:17 +0000 (17:37 -0800)
-Modify the carveout killer to only kill tasks with lower priorities
than the one that's trying to allocate
-After delivering a sigkill to a task, wait for something to exit and
cleanup before retrying the allocation

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

index 32008e21b01ed65f632a89bc7803f22a9497b839..674b34ab6f45e2d07a351df7c1359d7ef587dc04 100644 (file)
@@ -325,6 +325,7 @@ static struct nvmap_client* get_client_from_carveout_commit(
 }
 
 #ifdef CONFIG_NVMAP_CARVEOUT_KILLER
+static DECLARE_WAIT_QUEUE_HEAD(wait_reclaim);
 bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
 {
        struct nvmap_carveout_commit *commit;
@@ -332,7 +333,13 @@ bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
        int selected_oom_adj = OOM_ADJUST_MIN;
        struct task_struct *selected_task = NULL;
        unsigned long flags;
-       bool death_pending = false;
+       bool wait = false;
+       int current_oom_adj = OOM_ADJUST_MIN;
+
+       task_lock(current);
+       if (current->signal)
+               current_oom_adj = current->signal->oom_adj;
+       task_unlock(current);
 
        spin_lock_irqsave(&node->clients_lock, flags);
        /* find the task with the smallest oom_adj (lowest priority)
@@ -347,20 +354,27 @@ bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
 
                if (!task)
                        continue;
+
+               task_lock(task);
                sig = task->signal;
                if (!task->mm || !sig)
-                       continue;
+                       goto end;
+               /* don't try to kill higher priority tasks */
+               if (sig->oom_adj < current_oom_adj)
+                       goto end;
                if (sig->oom_adj < selected_oom_adj)
-                       continue;
+                       goto end;
                if (sig->oom_adj == selected_oom_adj &&
                    size <= selected_size)
-                       continue;
+                       goto end;
                selected_oom_adj = sig->oom_adj;
                selected_size = size;
                selected_task = task;
+end:
+               task_unlock(task);
        }
        if (selected_task) {
-               death_pending = selected_task == current;
+               wait = selected_task != current;
                if (fatal_signal_pending(selected_task)) {
                        pr_warning("carveout_killer: process %d dying "
                                   "slowly\n", selected_task->pid);
@@ -373,7 +387,7 @@ bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
        }
 out:
        spin_unlock_irqrestore(&node->clients_lock, flags);
-       return death_pending;
+       return wait;
 }
 #endif
 
@@ -420,6 +434,8 @@ struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
        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,
@@ -427,6 +443,13 @@ struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
                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);
+
                /* shrink carveouts that matter and try again */
                for (i = 0; i < dev->nr_carveouts; i++) {
                        co_heap = &dev->heaps[i];
@@ -435,13 +458,22 @@ struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
                                continue;
 
                        /* indicates we just delivered a sigkill to current,
-                          might as well stop trying so the process can exit */
-                       if (nvmap_shrink_carveout(co_heap))
+                          or 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);
                }
-               yield();
        } while (time_is_after_jiffies(end));
 
+       if (time_is_before_jiffies(end))
+           printk("carveout_killer: timeout expired without allocation "
+                  "succeeding.\n");
+
        return NULL;
 #else
        block = do_nvmap_carveout_alloc(client, len, align, usage, prot);
@@ -583,6 +615,7 @@ static void destroy_client(struct nvmap_client *client)
        if (!client)
                return;
 
+
        while ((n = rb_first(&client->handle_refs))) {
                struct nvmap_handle_ref *ref;
                int pins, dupes;
@@ -608,6 +641,10 @@ static void destroy_client(struct nvmap_client *client)
                kfree(ref);
        }
 
+#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
+       wake_up_all(&wait_reclaim);
+#endif
+
        for (i = 0; i < client->dev->nr_carveouts; i++)
                list_del(&client->carveout_commit[i].list);