summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/nvmap/nvmap_dev.c
diff options
context:
space:
mode:
authorRebecca Schultz Zavin <rebecca@android.com>2010-12-03 15:43:33 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:36:53 -0800
commitefd95dc2548b8128ca03dd11a321cd454744a6f2 (patch)
treed750986494744c20ae146f7ecd0f2d7f0ed12424 /drivers/video/tegra/nvmap/nvmap_dev.c
parent31d05b052d0fcabc34bb457153dc9aa500d2842d (diff)
video: tegra: nvmap: Add carveout killer
This change attempts to reclaim carveout memory by killing other carveout users when an allocation fails. Processes are killed in order of priority from lowest to highest, and then from largest to smallest users. Change-Id: Iee8a6f36269bc8165d691000a153dbf9f4337775 Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
Diffstat (limited to 'drivers/video/tegra/nvmap/nvmap_dev.c')
-rw-r--r--drivers/video/tegra/nvmap/nvmap_dev.c101
1 files changed, 97 insertions, 4 deletions
diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c
index 1961c714efe5..7b8228a5d956 100644
--- a/drivers/video/tegra/nvmap/nvmap_dev.c
+++ b/drivers/video/tegra/nvmap/nvmap_dev.c
@@ -23,9 +23,11 @@
#include <linux/backing-dev.h>
#include <linux/bitmap.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
+#include <linux/oom.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -44,6 +46,7 @@
#include "nvmap_mru.h"
#define NVMAP_NUM_PTES 64
+#define NVMAP_CARVEOUT_KILLER_RETRY_TIME 100 /* msecs */
struct nvmap_carveout_node {
unsigned int heap_bit;
@@ -321,10 +324,60 @@ static struct nvmap_client* get_client_from_carveout_commit(
carveout_commit);
}
-struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
- size_t len, size_t align,
- unsigned long usage,
- unsigned int prot)
+#ifdef CONFIG_NVMAP_CARVEOUT_KILLER
+bool nvmap_shrink_carveout(struct nvmap_carveout_node *node)
+{
+ struct nvmap_carveout_commit *commit;
+ size_t selected_size = 0;
+ int selected_oom_adj = OOM_ADJUST_MIN;
+ struct task_struct *selected_task = NULL;
+ unsigned long flags;
+ bool death_pending = false;
+
+ spin_lock_irqsave(&node->clients_lock, flags);
+ /* find the task with the smallest oom_adj (lowest priority)
+ * and largest carveout allocation -- ignore kernel allocations,
+ * there's no way to handle them */
+ list_for_each_entry(commit, &node->clients, list) {
+ struct nvmap_client *client =
+ get_client_from_carveout_commit(node, commit);
+ size_t size = commit->commit;
+ struct task_struct *task = client->task;
+ struct signal_struct *sig = task->signal;
+
+ if (!task->mm || !sig)
+ continue;
+ if (sig->oom_adj < selected_oom_adj)
+ continue;
+ if (sig->oom_adj == selected_oom_adj &&
+ size <= selected_size)
+ continue;
+ selected_oom_adj = sig->oom_adj;
+ selected_size = size;
+ selected_task = task;
+ }
+ if (selected_task) {
+ death_pending = selected_task == current;
+ 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);
+ force_sig(SIGKILL, selected_task);
+ }
+out:
+ spin_unlock_irqrestore(&node->clients_lock, flags);
+ return death_pending;
+}
+#endif
+
+struct nvmap_heap_block *do_nvmap_carveout_alloc(struct nvmap_client *client,
+ size_t len, size_t align,
+ unsigned long usage,
+ unsigned int prot)
{
struct nvmap_carveout_node *co_heap;
struct nvmap_device *dev = client->dev;
@@ -349,8 +402,48 @@ struct nvmap_heap_block *nvmap_carveout_alloc(struct nvmap_client *client,
return block;
}
}
+ return NULL;
+}
+
+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);
+
+ do {
+ block = do_nvmap_carveout_alloc(client, len, align, usage,
+ prot);
+ if (block)
+ return block;
+
+ /* shrink carveouts that matter and try again */
+ for (i = 0; i < dev->nr_carveouts; i++) {
+ co_heap = &dev->heaps[i];
+
+ if (!(co_heap->heap_bit & usage))
+ 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))
+ return NULL;
+ }
+ yield();
+ } while (time_is_after_jiffies(end));
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