mm/ksm.c

Source file repositories/reference/linux-study-clean/mm/ksm.c

File Facts

System
Linux kernel
Corpus path
mm/ksm.c
Extension
.c
Size
112035 bytes
Lines
4022
Domain
Core OS
Bucket
Memory Management
Inferred role
Core OS: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

ret = handle_mm_fault(vma, addr,
				      FAULT_FLAG_UNSHARE | FAULT_FLAG_REMOTE,
				      NULL);
	} while (!(ret & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | VM_FAULT_OOM)));
	/*
	 * We must loop until we no longer find a KSM page because
	 * handle_mm_fault() may back out if there's any difficulty e.g. if
	 * pte accessed bit gets updated concurrently.
	 *
	 * VM_FAULT_SIGBUS could occur if we race with truncation of the
	 * backing file, which also invalidates anonymous pages: that's
	 * okay, that truncation will have unmapped the KSM page for us.
	 *
	 * VM_FAULT_OOM: at the time of writing (late July 2009), setting
	 * aside mem_cgroup limits, VM_FAULT_OOM would only be set if the
	 * current task has TIF_MEMDIE set, and will be OOM killed on return
	 * to user; and ksmd, having no mm, would never be chosen for that.
	 *
	 * But if the mm is in a limited mem_cgroup, then the fault may fail
	 * with VM_FAULT_OOM even if the current task is not TIF_MEMDIE; and
	 * even ksmd can fail in this way - though it's usually breaking ksm
	 * just to undo a merge it made a moment before, so unlikely to oom.
	 *
	 * That's a pity: we might therefore have more kernel pages allocated
	 * than we're counting as nodes in the stable tree; but ksm_do_scan
	 * will retry to break_cow on each pass, so should recover the page
	 * in due course.  The important thing is to not let VM_MERGEABLE
	 * be cleared while any such pages might remain in the area.
	 */
	return (ret & VM_FAULT_OOM) ? -ENOMEM : 0;
}

static bool ksm_compatible(const struct file *file, vma_flags_t vma_flags)
{
	/* Just ignore the advice. */
	if (vma_flags_test_any(&vma_flags, VMA_SHARED_BIT, VMA_MAYSHARE_BIT,
			       VMA_HUGETLB_BIT))
		return false;
	if (vma_flags_test_single_mask(&vma_flags, VMA_DROPPABLE))
		return false;
	if (vma_flags_test_any_mask(&vma_flags, VMA_SPECIAL_FLAGS))
		return false;
	if (file_is_dax(file))
		return false;
#ifdef VM_SAO
	if (vma_flags_test(&vma_flags, VMA_SAO_BIT))
		return false;
#endif
#ifdef VM_SPARC_ADI
	if (vma_flags_test(&vma_flags, VMA_SPARC_ADI_BIT))
		return false;
#endif

	return true;
}

static bool vma_ksm_compatible(struct vm_area_struct *vma)
{
	return ksm_compatible(vma->vm_file, vma->flags);
}

static struct vm_area_struct *find_mergeable_vma(struct mm_struct *mm,
		unsigned long addr)
{
	struct vm_area_struct *vma;
	if (ksm_test_exit(mm))
		return NULL;
	vma = vma_lookup(mm, addr);
	if (!vma || !(vma->vm_flags & VM_MERGEABLE) || !vma->anon_vma)
		return NULL;
	return vma;
}

static void break_cow(struct ksm_rmap_item *rmap_item)
{
	struct mm_struct *mm = rmap_item->mm;
	unsigned long addr = rmap_item->address;
	struct vm_area_struct *vma;

	/*
	 * It is not an accident that whenever we want to break COW
	 * to undo, we also need to drop a reference to the anon_vma.
	 */
	put_anon_vma(rmap_item->anon_vma);

	mmap_read_lock(mm);
	vma = find_mergeable_vma(mm, addr);
	if (vma)
		break_ksm(vma, addr, addr + PAGE_SIZE, false);
	mmap_read_unlock(mm);

Annotation

Implementation Notes