|
@@ -2181,6 +2181,82 @@ static void sp_free(struct sp_node *n)
|
|
|
kmem_cache_free(sn_cache, n);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * mpol_misplaced - check whether current page node is valid in policy
|
|
|
+ *
|
|
|
+ * @page - page to be checked
|
|
|
+ * @vma - vm area where page mapped
|
|
|
+ * @addr - virtual address where page mapped
|
|
|
+ *
|
|
|
+ * Lookup current policy node id for vma,addr and "compare to" page's
|
|
|
+ * node id.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * -1 - not misplaced, page is in the right node
|
|
|
+ * node - node id where the page should be
|
|
|
+ *
|
|
|
+ * Policy determination "mimics" alloc_page_vma().
|
|
|
+ * Called from fault path where we know the vma and faulting address.
|
|
|
+ */
|
|
|
+int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long addr)
|
|
|
+{
|
|
|
+ struct mempolicy *pol;
|
|
|
+ struct zone *zone;
|
|
|
+ int curnid = page_to_nid(page);
|
|
|
+ unsigned long pgoff;
|
|
|
+ int polnid = -1;
|
|
|
+ int ret = -1;
|
|
|
+
|
|
|
+ BUG_ON(!vma);
|
|
|
+
|
|
|
+ pol = get_vma_policy(current, vma, addr);
|
|
|
+ if (!(pol->flags & MPOL_F_MOF))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ switch (pol->mode) {
|
|
|
+ case MPOL_INTERLEAVE:
|
|
|
+ BUG_ON(addr >= vma->vm_end);
|
|
|
+ BUG_ON(addr < vma->vm_start);
|
|
|
+
|
|
|
+ pgoff = vma->vm_pgoff;
|
|
|
+ pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
|
|
|
+ polnid = offset_il_node(pol, vma, pgoff);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MPOL_PREFERRED:
|
|
|
+ if (pol->flags & MPOL_F_LOCAL)
|
|
|
+ polnid = numa_node_id();
|
|
|
+ else
|
|
|
+ polnid = pol->v.preferred_node;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MPOL_BIND:
|
|
|
+ /*
|
|
|
+ * allows binding to multiple nodes.
|
|
|
+ * use current page if in policy nodemask,
|
|
|
+ * else select nearest allowed node, if any.
|
|
|
+ * If no allowed nodes, use current [!misplaced].
|
|
|
+ */
|
|
|
+ if (node_isset(curnid, pol->v.nodes))
|
|
|
+ goto out;
|
|
|
+ (void)first_zones_zonelist(
|
|
|
+ node_zonelist(numa_node_id(), GFP_HIGHUSER),
|
|
|
+ gfp_zone(GFP_HIGHUSER),
|
|
|
+ &pol->v.nodes, &zone);
|
|
|
+ polnid = zone->node;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+ if (curnid != polnid)
|
|
|
+ ret = polnid;
|
|
|
+out:
|
|
|
+ mpol_cond_put(pol);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void sp_delete(struct shared_policy *sp, struct sp_node *n)
|
|
|
{
|
|
|
pr_debug("deleting %lx-l%lx\n", n->start, n->end);
|