xref: /linux/mm/hmm.c (revision c0b124054f9e42eb6da545a10fe9122a7d7c3f72)
1133ff0eaSJérôme Glisse /*
2133ff0eaSJérôme Glisse  * Copyright 2013 Red Hat Inc.
3133ff0eaSJérôme Glisse  *
4133ff0eaSJérôme Glisse  * This program is free software; you can redistribute it and/or modify
5133ff0eaSJérôme Glisse  * it under the terms of the GNU General Public License as published by
6133ff0eaSJérôme Glisse  * the Free Software Foundation; either version 2 of the License, or
7133ff0eaSJérôme Glisse  * (at your option) any later version.
8133ff0eaSJérôme Glisse  *
9133ff0eaSJérôme Glisse  * This program is distributed in the hope that it will be useful,
10133ff0eaSJérôme Glisse  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11133ff0eaSJérôme Glisse  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12133ff0eaSJérôme Glisse  * GNU General Public License for more details.
13133ff0eaSJérôme Glisse  *
14133ff0eaSJérôme Glisse  * Authors: Jérôme Glisse <jglisse@redhat.com>
15133ff0eaSJérôme Glisse  */
16133ff0eaSJérôme Glisse /*
17133ff0eaSJérôme Glisse  * Refer to include/linux/hmm.h for information about heterogeneous memory
18133ff0eaSJérôme Glisse  * management or HMM for short.
19133ff0eaSJérôme Glisse  */
20133ff0eaSJérôme Glisse #include <linux/mm.h>
21133ff0eaSJérôme Glisse #include <linux/hmm.h>
22133ff0eaSJérôme Glisse #include <linux/slab.h>
23133ff0eaSJérôme Glisse #include <linux/sched.h>
24*c0b12405SJérôme Glisse #include <linux/mmu_notifier.h>
25133ff0eaSJérôme Glisse 
26133ff0eaSJérôme Glisse 
27133ff0eaSJérôme Glisse #ifdef CONFIG_HMM
28*c0b12405SJérôme Glisse static const struct mmu_notifier_ops hmm_mmu_notifier_ops;
29*c0b12405SJérôme Glisse 
30133ff0eaSJérôme Glisse /*
31133ff0eaSJérôme Glisse  * struct hmm - HMM per mm struct
32133ff0eaSJérôme Glisse  *
33133ff0eaSJérôme Glisse  * @mm: mm struct this HMM struct is bound to
34*c0b12405SJérôme Glisse  * @sequence: we track updates to the CPU page table with a sequence number
35*c0b12405SJérôme Glisse  * @mirrors: list of mirrors for this mm
36*c0b12405SJérôme Glisse  * @mmu_notifier: mmu notifier to track updates to CPU page table
37*c0b12405SJérôme Glisse  * @mirrors_sem: read/write semaphore protecting the mirrors list
38133ff0eaSJérôme Glisse  */
39133ff0eaSJérôme Glisse struct hmm {
40133ff0eaSJérôme Glisse 	struct mm_struct	*mm;
41*c0b12405SJérôme Glisse 	atomic_t		sequence;
42*c0b12405SJérôme Glisse 	struct list_head	mirrors;
43*c0b12405SJérôme Glisse 	struct mmu_notifier	mmu_notifier;
44*c0b12405SJérôme Glisse 	struct rw_semaphore	mirrors_sem;
45133ff0eaSJérôme Glisse };
46133ff0eaSJérôme Glisse 
47133ff0eaSJérôme Glisse /*
48133ff0eaSJérôme Glisse  * hmm_register - register HMM against an mm (HMM internal)
49133ff0eaSJérôme Glisse  *
50133ff0eaSJérôme Glisse  * @mm: mm struct to attach to
51133ff0eaSJérôme Glisse  *
52133ff0eaSJérôme Glisse  * This is not intended to be used directly by device drivers. It allocates an
53133ff0eaSJérôme Glisse  * HMM struct if mm does not have one, and initializes it.
54133ff0eaSJérôme Glisse  */
55133ff0eaSJérôme Glisse static struct hmm *hmm_register(struct mm_struct *mm)
56133ff0eaSJérôme Glisse {
57*c0b12405SJérôme Glisse 	struct hmm *hmm = READ_ONCE(mm->hmm);
58*c0b12405SJérôme Glisse 	bool cleanup = false;
59133ff0eaSJérôme Glisse 
60133ff0eaSJérôme Glisse 	/*
61133ff0eaSJérôme Glisse 	 * The hmm struct can only be freed once the mm_struct goes away,
62133ff0eaSJérôme Glisse 	 * hence we should always have pre-allocated an new hmm struct
63133ff0eaSJérôme Glisse 	 * above.
64133ff0eaSJérôme Glisse 	 */
65*c0b12405SJérôme Glisse 	if (hmm)
66*c0b12405SJérôme Glisse 		return hmm;
67*c0b12405SJérôme Glisse 
68*c0b12405SJérôme Glisse 	hmm = kmalloc(sizeof(*hmm), GFP_KERNEL);
69*c0b12405SJérôme Glisse 	if (!hmm)
70*c0b12405SJérôme Glisse 		return NULL;
71*c0b12405SJérôme Glisse 	INIT_LIST_HEAD(&hmm->mirrors);
72*c0b12405SJérôme Glisse 	init_rwsem(&hmm->mirrors_sem);
73*c0b12405SJérôme Glisse 	atomic_set(&hmm->sequence, 0);
74*c0b12405SJérôme Glisse 	hmm->mmu_notifier.ops = NULL;
75*c0b12405SJérôme Glisse 	hmm->mm = mm;
76*c0b12405SJérôme Glisse 
77*c0b12405SJérôme Glisse 	/*
78*c0b12405SJérôme Glisse 	 * We should only get here if hold the mmap_sem in write mode ie on
79*c0b12405SJérôme Glisse 	 * registration of first mirror through hmm_mirror_register()
80*c0b12405SJérôme Glisse 	 */
81*c0b12405SJérôme Glisse 	hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
82*c0b12405SJérôme Glisse 	if (__mmu_notifier_register(&hmm->mmu_notifier, mm)) {
83*c0b12405SJérôme Glisse 		kfree(hmm);
84*c0b12405SJérôme Glisse 		return NULL;
85*c0b12405SJérôme Glisse 	}
86*c0b12405SJérôme Glisse 
87*c0b12405SJérôme Glisse 	spin_lock(&mm->page_table_lock);
88*c0b12405SJérôme Glisse 	if (!mm->hmm)
89*c0b12405SJérôme Glisse 		mm->hmm = hmm;
90*c0b12405SJérôme Glisse 	else
91*c0b12405SJérôme Glisse 		cleanup = true;
92*c0b12405SJérôme Glisse 	spin_unlock(&mm->page_table_lock);
93*c0b12405SJérôme Glisse 
94*c0b12405SJérôme Glisse 	if (cleanup) {
95*c0b12405SJérôme Glisse 		mmu_notifier_unregister(&hmm->mmu_notifier, mm);
96*c0b12405SJérôme Glisse 		kfree(hmm);
97*c0b12405SJérôme Glisse 	}
98*c0b12405SJérôme Glisse 
99133ff0eaSJérôme Glisse 	return mm->hmm;
100133ff0eaSJérôme Glisse }
101133ff0eaSJérôme Glisse 
102133ff0eaSJérôme Glisse void hmm_mm_destroy(struct mm_struct *mm)
103133ff0eaSJérôme Glisse {
104133ff0eaSJérôme Glisse 	kfree(mm->hmm);
105133ff0eaSJérôme Glisse }
106133ff0eaSJérôme Glisse #endif /* CONFIG_HMM */
107*c0b12405SJérôme Glisse 
108*c0b12405SJérôme Glisse #if IS_ENABLED(CONFIG_HMM_MIRROR)
109*c0b12405SJérôme Glisse static void hmm_invalidate_range(struct hmm *hmm,
110*c0b12405SJérôme Glisse 				 enum hmm_update_type action,
111*c0b12405SJérôme Glisse 				 unsigned long start,
112*c0b12405SJérôme Glisse 				 unsigned long end)
113*c0b12405SJérôme Glisse {
114*c0b12405SJérôme Glisse 	struct hmm_mirror *mirror;
115*c0b12405SJérôme Glisse 
116*c0b12405SJérôme Glisse 	down_read(&hmm->mirrors_sem);
117*c0b12405SJérôme Glisse 	list_for_each_entry(mirror, &hmm->mirrors, list)
118*c0b12405SJérôme Glisse 		mirror->ops->sync_cpu_device_pagetables(mirror, action,
119*c0b12405SJérôme Glisse 							start, end);
120*c0b12405SJérôme Glisse 	up_read(&hmm->mirrors_sem);
121*c0b12405SJérôme Glisse }
122*c0b12405SJérôme Glisse 
123*c0b12405SJérôme Glisse static void hmm_invalidate_range_start(struct mmu_notifier *mn,
124*c0b12405SJérôme Glisse 				       struct mm_struct *mm,
125*c0b12405SJérôme Glisse 				       unsigned long start,
126*c0b12405SJérôme Glisse 				       unsigned long end)
127*c0b12405SJérôme Glisse {
128*c0b12405SJérôme Glisse 	struct hmm *hmm = mm->hmm;
129*c0b12405SJérôme Glisse 
130*c0b12405SJérôme Glisse 	VM_BUG_ON(!hmm);
131*c0b12405SJérôme Glisse 
132*c0b12405SJérôme Glisse 	atomic_inc(&hmm->sequence);
133*c0b12405SJérôme Glisse }
134*c0b12405SJérôme Glisse 
135*c0b12405SJérôme Glisse static void hmm_invalidate_range_end(struct mmu_notifier *mn,
136*c0b12405SJérôme Glisse 				     struct mm_struct *mm,
137*c0b12405SJérôme Glisse 				     unsigned long start,
138*c0b12405SJérôme Glisse 				     unsigned long end)
139*c0b12405SJérôme Glisse {
140*c0b12405SJérôme Glisse 	struct hmm *hmm = mm->hmm;
141*c0b12405SJérôme Glisse 
142*c0b12405SJérôme Glisse 	VM_BUG_ON(!hmm);
143*c0b12405SJérôme Glisse 
144*c0b12405SJérôme Glisse 	hmm_invalidate_range(mm->hmm, HMM_UPDATE_INVALIDATE, start, end);
145*c0b12405SJérôme Glisse }
146*c0b12405SJérôme Glisse 
147*c0b12405SJérôme Glisse static const struct mmu_notifier_ops hmm_mmu_notifier_ops = {
148*c0b12405SJérôme Glisse 	.invalidate_range_start	= hmm_invalidate_range_start,
149*c0b12405SJérôme Glisse 	.invalidate_range_end	= hmm_invalidate_range_end,
150*c0b12405SJérôme Glisse };
151*c0b12405SJérôme Glisse 
152*c0b12405SJérôme Glisse /*
153*c0b12405SJérôme Glisse  * hmm_mirror_register() - register a mirror against an mm
154*c0b12405SJérôme Glisse  *
155*c0b12405SJérôme Glisse  * @mirror: new mirror struct to register
156*c0b12405SJérôme Glisse  * @mm: mm to register against
157*c0b12405SJérôme Glisse  *
158*c0b12405SJérôme Glisse  * To start mirroring a process address space, the device driver must register
159*c0b12405SJérôme Glisse  * an HMM mirror struct.
160*c0b12405SJérôme Glisse  *
161*c0b12405SJérôme Glisse  * THE mm->mmap_sem MUST BE HELD IN WRITE MODE !
162*c0b12405SJérôme Glisse  */
163*c0b12405SJérôme Glisse int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm)
164*c0b12405SJérôme Glisse {
165*c0b12405SJérôme Glisse 	/* Sanity check */
166*c0b12405SJérôme Glisse 	if (!mm || !mirror || !mirror->ops)
167*c0b12405SJérôme Glisse 		return -EINVAL;
168*c0b12405SJérôme Glisse 
169*c0b12405SJérôme Glisse 	mirror->hmm = hmm_register(mm);
170*c0b12405SJérôme Glisse 	if (!mirror->hmm)
171*c0b12405SJérôme Glisse 		return -ENOMEM;
172*c0b12405SJérôme Glisse 
173*c0b12405SJérôme Glisse 	down_write(&mirror->hmm->mirrors_sem);
174*c0b12405SJérôme Glisse 	list_add(&mirror->list, &mirror->hmm->mirrors);
175*c0b12405SJérôme Glisse 	up_write(&mirror->hmm->mirrors_sem);
176*c0b12405SJérôme Glisse 
177*c0b12405SJérôme Glisse 	return 0;
178*c0b12405SJérôme Glisse }
179*c0b12405SJérôme Glisse EXPORT_SYMBOL(hmm_mirror_register);
180*c0b12405SJérôme Glisse 
181*c0b12405SJérôme Glisse /*
182*c0b12405SJérôme Glisse  * hmm_mirror_unregister() - unregister a mirror
183*c0b12405SJérôme Glisse  *
184*c0b12405SJérôme Glisse  * @mirror: new mirror struct to register
185*c0b12405SJérôme Glisse  *
186*c0b12405SJérôme Glisse  * Stop mirroring a process address space, and cleanup.
187*c0b12405SJérôme Glisse  */
188*c0b12405SJérôme Glisse void hmm_mirror_unregister(struct hmm_mirror *mirror)
189*c0b12405SJérôme Glisse {
190*c0b12405SJérôme Glisse 	struct hmm *hmm = mirror->hmm;
191*c0b12405SJérôme Glisse 
192*c0b12405SJérôme Glisse 	down_write(&hmm->mirrors_sem);
193*c0b12405SJérôme Glisse 	list_del(&mirror->list);
194*c0b12405SJérôme Glisse 	up_write(&hmm->mirrors_sem);
195*c0b12405SJérôme Glisse }
196*c0b12405SJérôme Glisse EXPORT_SYMBOL(hmm_mirror_unregister);
197*c0b12405SJérôme Glisse #endif /* IS_ENABLED(CONFIG_HMM_MIRROR) */
198