Blame view

mm/hmm.c 29.3 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
2
3
4
  /*
   * Copyright 2013 Red Hat Inc.
   *
f813f2197   Jérôme Glisse   mm/hmm: fix utf8 ...
5
   * Authors: Jérôme Glisse <jglisse@redhat.com>
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
6
7
8
9
10
   */
  /*
   * Refer to include/linux/hmm.h for information about heterogeneous memory
   * management or HMM for short.
   */
a520110e4   Christoph Hellwig   mm: split out a n...
11
  #include <linux/pagewalk.h>
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
12
  #include <linux/hmm.h>
858b54dab   Jérôme Glisse   mm/hmm/devmem: du...
13
  #include <linux/init.h>
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
14
15
  #include <linux/rmap.h>
  #include <linux/swap.h>
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
16
17
  #include <linux/slab.h>
  #include <linux/sched.h>
4ef589dc9   Jérôme Glisse   mm/hmm/devmem: de...
18
19
  #include <linux/mmzone.h>
  #include <linux/pagemap.h>
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
20
21
  #include <linux/swapops.h>
  #include <linux/hugetlb.h>
4ef589dc9   Jérôme Glisse   mm/hmm/devmem: de...
22
  #include <linux/memremap.h>
c8a53b2db   Jason Gunthorpe   mm/hmm: Hold a mm...
23
  #include <linux/sched/mm.h>
7b2d55d2c   Jérôme Glisse   mm/ZONE_DEVICE: s...
24
  #include <linux/jump_label.h>
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
25
  #include <linux/dma-mapping.h>
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
26
  #include <linux/mmu_notifier.h>
4ef589dc9   Jérôme Glisse   mm/hmm/devmem: de...
27
  #include <linux/memory_hotplug.h>
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
28
  static struct mmu_notifier *hmm_alloc_notifier(struct mm_struct *mm)
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
29
  {
8a9320b7e   Jason Gunthorpe   mm/hmm: Simplify ...
30
  	struct hmm *hmm;
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
31

c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
32
  	hmm = kzalloc(sizeof(*hmm), GFP_KERNEL);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
33
  	if (!hmm)
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
34
  		return ERR_PTR(-ENOMEM);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
35
  	init_waitqueue_head(&hmm->wq);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
36
37
  	INIT_LIST_HEAD(&hmm->mirrors);
  	init_rwsem(&hmm->mirrors_sem);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
38
  	INIT_LIST_HEAD(&hmm->ranges);
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
39
  	spin_lock_init(&hmm->ranges_lock);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
40
  	hmm->notifiers = 0;
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
41
  	return &hmm->mmu_notifier;
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
42
  }
86a2d5984   Ralph Campbell   mm/hmm: fix race ...
43

c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
44
  static void hmm_free_notifier(struct mmu_notifier *mn)
6d7c3cde9   Jason Gunthorpe   mm/hmm: fix use a...
45
  {
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
46
  	struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
8a9320b7e   Jason Gunthorpe   mm/hmm: Simplify ...
47

c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
48
49
  	WARN_ON(!list_empty(&hmm->ranges));
  	WARN_ON(!list_empty(&hmm->mirrors));
86a2d5984   Ralph Campbell   mm/hmm: fix race ...
50
  	kfree(hmm);
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
51
  }
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
52
  static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm)
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
53
  {
6d7c3cde9   Jason Gunthorpe   mm/hmm: fix use a...
54
  	struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
55
  	struct hmm_mirror *mirror;
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
56

47f245985   Jason Gunthorpe   mm/hmm: Hold on t...
57
58
59
60
61
  	/*
  	 * Since hmm_range_register() holds the mmget() lock hmm_release() is
  	 * prevented as long as a range exists.
  	 */
  	WARN_ON(!list_empty_careful(&hmm->ranges));
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
62

14331726a   Jason Gunthorpe   mm/hmm: Remove co...
63
64
65
66
67
68
69
  	down_read(&hmm->mirrors_sem);
  	list_for_each_entry(mirror, &hmm->mirrors, list) {
  		/*
  		 * Note: The driver is not allowed to trigger
  		 * hmm_mirror_unregister() from this thread.
  		 */
  		if (mirror->ops->release)
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
70
  			mirror->ops->release(mirror);
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
71
  	}
14331726a   Jason Gunthorpe   mm/hmm: Remove co...
72
  	up_read(&hmm->mirrors_sem);
133ff0eac   Jérôme Glisse   mm/hmm: heterogen...
73
  }
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
74

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
75
  static void notifiers_decrement(struct hmm *hmm)
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
76
  {
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
77
  	unsigned long flags;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
78

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
79
80
81
82
  	spin_lock_irqsave(&hmm->ranges_lock, flags);
  	hmm->notifiers--;
  	if (!hmm->notifiers) {
  		struct hmm_range *range;
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
83

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
84
85
86
87
  		list_for_each_entry(range, &hmm->ranges, list) {
  			if (range->valid)
  				continue;
  			range->valid = true;
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
88
  		}
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
89
  		wake_up_all(&hmm->wq);
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
90
  	}
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
91
  	spin_unlock_irqrestore(&hmm->ranges_lock, flags);
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
92
  }
93065ac75   Michal Hocko   mm, oom: distingu...
93
  static int hmm_invalidate_range_start(struct mmu_notifier *mn,
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
94
  			const struct mmu_notifier_range *nrange)
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
95
  {
6d7c3cde9   Jason Gunthorpe   mm/hmm: fix use a...
96
  	struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
97
  	struct hmm_mirror *mirror;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
98
  	struct hmm_range *range;
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
99
  	unsigned long flags;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
100
  	int ret = 0;
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
101

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
102
  	spin_lock_irqsave(&hmm->ranges_lock, flags);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
103
104
  	hmm->notifiers++;
  	list_for_each_entry(range, &hmm->ranges, list) {
1f9618079   Ralph Campbell   mm/hmm: replace h...
105
  		if (nrange->end < range->start || nrange->start >= range->end)
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
106
107
108
109
  			continue;
  
  		range->valid = false;
  	}
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
110
  	spin_unlock_irqrestore(&hmm->ranges_lock, flags);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
111

dfcd66604   Jérôme Glisse   mm/mmu_notifier: ...
112
  	if (mmu_notifier_range_blockable(nrange))
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
113
114
115
116
117
  		down_read(&hmm->mirrors_sem);
  	else if (!down_read_trylock(&hmm->mirrors_sem)) {
  		ret = -EAGAIN;
  		goto out;
  	}
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
118

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
119
  	list_for_each_entry(mirror, &hmm->mirrors, list) {
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
120
  		int rc;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
121

1f9618079   Ralph Campbell   mm/hmm: replace h...
122
  		rc = mirror->ops->sync_cpu_device_pagetables(mirror, nrange);
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
123
  		if (rc) {
1f9618079   Ralph Campbell   mm/hmm: replace h...
124
125
  			if (WARN_ON(mmu_notifier_range_blockable(nrange) ||
  			    rc != -EAGAIN))
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
126
  				continue;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
127
  			ret = -EAGAIN;
085ea2506   Ralph Campbell   mm/hmm: clean up ...
128
  			break;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
129
130
131
132
133
  		}
  	}
  	up_read(&hmm->mirrors_sem);
  
  out:
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
134
135
  	if (ret)
  		notifiers_decrement(hmm);
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
136
  	return ret;
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
137
138
139
  }
  
  static void hmm_invalidate_range_end(struct mmu_notifier *mn,
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
140
  			const struct mmu_notifier_range *nrange)
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
141
  {
6d7c3cde9   Jason Gunthorpe   mm/hmm: fix use a...
142
  	struct hmm *hmm = container_of(mn, struct hmm, mmu_notifier);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
143

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
144
  	notifiers_decrement(hmm);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
145
146
147
  }
  
  static const struct mmu_notifier_ops hmm_mmu_notifier_ops = {
e1401513c   Ralph Campbell   mm/hmm: HMM shoul...
148
  	.release		= hmm_release,
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
149
150
  	.invalidate_range_start	= hmm_invalidate_range_start,
  	.invalidate_range_end	= hmm_invalidate_range_end,
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
151
152
  	.alloc_notifier		= hmm_alloc_notifier,
  	.free_notifier		= hmm_free_notifier,
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
153
154
155
156
157
158
159
  };
  
  /*
   * hmm_mirror_register() - register a mirror against an mm
   *
   * @mirror: new mirror struct to register
   * @mm: mm to register against
085ea2506   Ralph Campbell   mm/hmm: clean up ...
160
   * Return: 0 on success, -ENOMEM if no memory, -EINVAL if invalid arguments
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
161
162
163
   *
   * To start mirroring a process address space, the device driver must register
   * an HMM mirror struct.
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
164
165
166
167
168
169
   *
   * The caller cannot unregister the hmm_mirror while any ranges are
   * registered.
   *
   * Callers using this function must put a call to mmu_notifier_synchronize()
   * in their module exit functions.
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
170
171
172
   */
  int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm)
  {
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
173
  	struct mmu_notifier *mn;
fec88ab0a   Linus Torvalds   Merge tag 'for-li...
174
  	lockdep_assert_held_write(&mm->mmap_sem);
8a1a0cd0b   Jason Gunthorpe   mm/hmm: Use lockd...
175

c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
176
177
178
  	/* Sanity check */
  	if (!mm || !mirror || !mirror->ops)
  		return -EINVAL;
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
179
180
181
182
  	mn = mmu_notifier_get_locked(&hmm_mmu_notifier_ops, mm);
  	if (IS_ERR(mn))
  		return PTR_ERR(mn);
  	mirror->hmm = container_of(mn, struct hmm, mmu_notifier);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
183
184
  
  	down_write(&mirror->hmm->mirrors_sem);
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
185
186
  	list_add(&mirror->list, &mirror->hmm->mirrors);
  	up_write(&mirror->hmm->mirrors_sem);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
187
188
189
190
191
192
193
194
  
  	return 0;
  }
  EXPORT_SYMBOL(hmm_mirror_register);
  
  /*
   * hmm_mirror_unregister() - unregister a mirror
   *
085ea2506   Ralph Campbell   mm/hmm: clean up ...
195
   * @mirror: mirror struct to unregister
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
196
197
198
199
200
   *
   * Stop mirroring a process address space, and cleanup.
   */
  void hmm_mirror_unregister(struct hmm_mirror *mirror)
  {
187229c2d   Jason Gunthorpe   mm/hmm: Remove ra...
201
  	struct hmm *hmm = mirror->hmm;
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
202
203
  
  	down_write(&hmm->mirrors_sem);
14331726a   Jason Gunthorpe   mm/hmm: Remove co...
204
  	list_del(&mirror->list);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
205
  	up_write(&hmm->mirrors_sem);
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
206
  	mmu_notifier_put(&hmm->mmu_notifier);
c0b124054   Jérôme Glisse   mm/hmm/mirror: mi...
207
208
  }
  EXPORT_SYMBOL(hmm_mirror_unregister);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
209

74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
210
211
  struct hmm_vma_walk {
  	struct hmm_range	*range;
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
212
  	struct dev_pagemap	*pgmap;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
213
  	unsigned long		last;
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
214
  	unsigned int		flags;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
215
  };
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
216
217
  static int hmm_vma_do_fault(struct mm_walk *walk, unsigned long addr,
  			    bool write_fault, uint64_t *pfn)
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
218
  {
9b1ae605c   Kuehling, Felix   mm/hmm: Only set ...
219
  	unsigned int flags = FAULT_FLAG_REMOTE;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
220
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
221
  	struct hmm_range *range = hmm_vma_walk->range;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
222
  	struct vm_area_struct *vma = walk->vma;
50a7ca3c6   Souptick Joarder   mm: convert retur...
223
  	vm_fault_t ret;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
224

6c64f2bbe   Ralph Campbell   mm/hmm: hmm_range...
225
226
  	if (!vma)
  		goto err;
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
227
228
229
230
  	if (hmm_vma_walk->flags & HMM_FAULT_ALLOW_RETRY)
  		flags |= FAULT_FLAG_ALLOW_RETRY;
  	if (write_fault)
  		flags |= FAULT_FLAG_WRITE;
50a7ca3c6   Souptick Joarder   mm: convert retur...
231
  	ret = handle_mm_fault(vma, addr, flags);
e709accc7   Jason Gunthorpe   mm/hmm: comment o...
232
233
  	if (ret & VM_FAULT_RETRY) {
  		/* Note, handle_mm_fault did up_read(&mm->mmap_sem)) */
73231612d   Jérôme Glisse   mm/hmm: improve a...
234
  		return -EAGAIN;
e709accc7   Jason Gunthorpe   mm/hmm: comment o...
235
  	}
6c64f2bbe   Ralph Campbell   mm/hmm: hmm_range...
236
237
  	if (ret & VM_FAULT_ERROR)
  		goto err;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
238

73231612d   Jérôme Glisse   mm/hmm: improve a...
239
  	return -EBUSY;
6c64f2bbe   Ralph Campbell   mm/hmm: hmm_range...
240
241
242
243
  
  err:
  	*pfn = range->values[HMM_PFN_ERROR];
  	return -EFAULT;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
244
  }
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
245
246
247
248
  static int hmm_pfns_bad(unsigned long addr,
  			unsigned long end,
  			struct mm_walk *walk)
  {
c719547f0   Jérôme Glisse   mm/hmm: hmm_pfns_...
249
250
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
ff05c0c6b   Jérôme Glisse   mm/hmm: use uint6...
251
  	uint64_t *pfns = range->pfns;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
252
253
254
255
  	unsigned long i;
  
  	i = (addr - range->start) >> PAGE_SHIFT;
  	for (; addr < end; addr += PAGE_SIZE, i++)
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
256
  		pfns[i] = range->values[HMM_PFN_ERROR];
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
257
258
259
  
  	return 0;
  }
5504ed296   Jérôme Glisse   mm/hmm: do not di...
260
  /*
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
261
262
   * hmm_vma_walk_hole_() - handle a range lacking valid pmd or pte(s)
   * @addr: range virtual start address (inclusive)
5504ed296   Jérôme Glisse   mm/hmm: do not di...
263
   * @end: range virtual end address (exclusive)
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
264
265
   * @fault: should we fault or not ?
   * @write_fault: write fault ?
5504ed296   Jérôme Glisse   mm/hmm: do not di...
266
   * @walk: mm_walk structure
085ea2506   Ralph Campbell   mm/hmm: clean up ...
267
   * Return: 0 on success, -EBUSY after page fault, or page fault error
5504ed296   Jérôme Glisse   mm/hmm: do not di...
268
269
270
271
   *
   * This function will be called whenever pmd_none() or pte_none() returns true,
   * or whenever there is no page directory covering the virtual address range.
   */
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
272
273
274
  static int hmm_vma_walk_hole_(unsigned long addr, unsigned long end,
  			      bool fault, bool write_fault,
  			      struct mm_walk *walk)
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
275
  {
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
276
277
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
ff05c0c6b   Jérôme Glisse   mm/hmm: use uint6...
278
  	uint64_t *pfns = range->pfns;
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
279
  	unsigned long i;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
280

74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
281
  	hmm_vma_walk->last = addr;
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
282
  	i = (addr - range->start) >> PAGE_SHIFT;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
283

c18ce674d   Ralph Campbell   mm/hmm: hmm_range...
284
285
  	if (write_fault && walk->vma && !(walk->vma->vm_flags & VM_WRITE))
  		return -EPERM;
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
286
  	for (; addr < end; addr += PAGE_SIZE, i++) {
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
287
  		pfns[i] = range->values[HMM_PFN_NONE];
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
288
  		if (fault || write_fault) {
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
289
  			int ret;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
290

2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
291
292
  			ret = hmm_vma_do_fault(walk, addr, write_fault,
  					       &pfns[i]);
73231612d   Jérôme Glisse   mm/hmm: improve a...
293
  			if (ret != -EBUSY)
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
294
295
296
  				return ret;
  		}
  	}
73231612d   Jérôme Glisse   mm/hmm: improve a...
297
  	return (fault || write_fault) ? -EBUSY : 0;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
298
299
300
301
302
303
  }
  
  static inline void hmm_pte_need_fault(const struct hmm_vma_walk *hmm_vma_walk,
  				      uint64_t pfns, uint64_t cpu_flags,
  				      bool *fault, bool *write_fault)
  {
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
304
  	struct hmm_range *range = hmm_vma_walk->range;
d45d464b1   Christoph Hellwig   mm/hmm: merge hmm...
305
  	if (hmm_vma_walk->flags & HMM_FAULT_SNAPSHOT)
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
306
  		return;
023a019a9   Jérôme Glisse   mm/hmm: add defau...
307
308
309
  	/*
  	 * So we not only consider the individual per page request we also
  	 * consider the default flags requested for the range. The API can
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
310
311
312
  	 * be used 2 ways. The first one where the HMM user coalesces
  	 * multiple page faults into one request and sets flags per pfn for
  	 * those faults. The second one where the HMM user wants to pre-
023a019a9   Jérôme Glisse   mm/hmm: add defau...
313
314
315
316
317
  	 * fault a range with specific flags. For the latter one it is a
  	 * waste to have the user pre-fill the pfn arrays with a default
  	 * flags value.
  	 */
  	pfns = (pfns & range->pfn_flags_mask) | range->default_flags;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
318
  	/* We aren't ask to do anything ... */
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
319
  	if (!(pfns & range->flags[HMM_PFN_VALID]))
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
320
  		return;
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
321
  	/* If this is device memory then only fault if explicitly requested */
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
322
323
324
325
326
327
  	if ((cpu_flags & range->flags[HMM_PFN_DEVICE_PRIVATE])) {
  		/* Do we fault on device memory ? */
  		if (pfns & range->flags[HMM_PFN_DEVICE_PRIVATE]) {
  			*write_fault = pfns & range->flags[HMM_PFN_WRITE];
  			*fault = true;
  		}
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
328
329
  		return;
  	}
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
330
331
332
333
334
335
336
  
  	/* If CPU page table is not valid then we need to fault */
  	*fault = !(cpu_flags & range->flags[HMM_PFN_VALID]);
  	/* Need to write fault ? */
  	if ((pfns & range->flags[HMM_PFN_WRITE]) &&
  	    !(cpu_flags & range->flags[HMM_PFN_WRITE])) {
  		*write_fault = true;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
337
338
339
340
341
342
343
344
345
346
  		*fault = true;
  	}
  }
  
  static void hmm_range_need_fault(const struct hmm_vma_walk *hmm_vma_walk,
  				 const uint64_t *pfns, unsigned long npages,
  				 uint64_t cpu_flags, bool *fault,
  				 bool *write_fault)
  {
  	unsigned long i;
d45d464b1   Christoph Hellwig   mm/hmm: merge hmm...
347
  	if (hmm_vma_walk->flags & HMM_FAULT_SNAPSHOT) {
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
348
349
350
  		*fault = *write_fault = false;
  		return;
  	}
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
351
  	*fault = *write_fault = false;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
352
353
354
  	for (i = 0; i < npages; ++i) {
  		hmm_pte_need_fault(hmm_vma_walk, pfns[i], cpu_flags,
  				   fault, write_fault);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
355
  		if ((*write_fault))
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  			return;
  	}
  }
  
  static int hmm_vma_walk_hole(unsigned long addr, unsigned long end,
  			     struct mm_walk *walk)
  {
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
  	bool fault, write_fault;
  	unsigned long i, npages;
  	uint64_t *pfns;
  
  	i = (addr - range->start) >> PAGE_SHIFT;
  	npages = (end - addr) >> PAGE_SHIFT;
  	pfns = &range->pfns[i];
  	hmm_range_need_fault(hmm_vma_walk, pfns, npages,
  			     0, &fault, &write_fault);
  	return hmm_vma_walk_hole_(addr, end, fault, write_fault, walk);
  }
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
376
  static inline uint64_t pmd_to_hmm_pfn_flags(struct hmm_range *range, pmd_t pmd)
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
377
378
379
  {
  	if (pmd_protnone(pmd))
  		return 0;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
380
381
382
  	return pmd_write(pmd) ? range->flags[HMM_PFN_VALID] |
  				range->flags[HMM_PFN_WRITE] :
  				range->flags[HMM_PFN_VALID];
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
383
  }
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
384
  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
9d3973d60   Christoph Hellwig   mm/hmm: cleanup t...
385
386
387
  static int hmm_vma_handle_pmd(struct mm_walk *walk, unsigned long addr,
  		unsigned long end, uint64_t *pfns, pmd_t pmd)
  {
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
388
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
389
  	struct hmm_range *range = hmm_vma_walk->range;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
390
  	unsigned long pfn, npages, i;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
391
  	bool fault, write_fault;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
392
  	uint64_t cpu_flags;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
393

2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
394
  	npages = (end - addr) >> PAGE_SHIFT;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
395
  	cpu_flags = pmd_to_hmm_pfn_flags(range, pmd);
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
396
397
  	hmm_range_need_fault(hmm_vma_walk, pfns, npages, cpu_flags,
  			     &fault, &write_fault);
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
398

2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
399
400
  	if (pmd_protnone(pmd) || fault || write_fault)
  		return hmm_vma_walk_hole_(addr, end, fault, write_fault, walk);
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
401

309f9a4f5   Christoph Hellwig   mm/hmm: don't abu...
402
  	pfn = pmd_pfn(pmd) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
403
404
405
406
407
408
409
  	for (i = 0; addr < end; addr += PAGE_SIZE, i++, pfn++) {
  		if (pmd_devmap(pmd)) {
  			hmm_vma_walk->pgmap = get_dev_pagemap(pfn,
  					      hmm_vma_walk->pgmap);
  			if (unlikely(!hmm_vma_walk->pgmap))
  				return -EBUSY;
  		}
391aab11e   Jérôme Glisse   mm/hmm: convert v...
410
  		pfns[i] = hmm_device_entry_from_pfn(range, pfn) | cpu_flags;
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
411
412
413
414
415
  	}
  	if (hmm_vma_walk->pgmap) {
  		put_dev_pagemap(hmm_vma_walk->pgmap);
  		hmm_vma_walk->pgmap = NULL;
  	}
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
416
417
418
  	hmm_vma_walk->last = end;
  	return 0;
  }
9d3973d60   Christoph Hellwig   mm/hmm: cleanup t...
419
420
421
422
423
  #else /* CONFIG_TRANSPARENT_HUGEPAGE */
  /* stub to allow the code below to compile */
  int hmm_vma_handle_pmd(struct mm_walk *walk, unsigned long addr,
  		unsigned long end, uint64_t *pfns, pmd_t pmd);
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
424

f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
425
  static inline uint64_t pte_to_hmm_pfn_flags(struct hmm_range *range, pte_t pte)
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
426
  {
789c2af88   Philip Yang   mm/hmm: support a...
427
  	if (pte_none(pte) || !pte_present(pte) || pte_protnone(pte))
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
428
  		return 0;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
429
430
431
  	return pte_write(pte) ? range->flags[HMM_PFN_VALID] |
  				range->flags[HMM_PFN_WRITE] :
  				range->flags[HMM_PFN_VALID];
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
432
  }
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
433
434
435
436
437
  static int hmm_vma_handle_pte(struct mm_walk *walk, unsigned long addr,
  			      unsigned long end, pmd_t *pmdp, pte_t *ptep,
  			      uint64_t *pfn)
  {
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
438
  	struct hmm_range *range = hmm_vma_walk->range;
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
439
440
  	bool fault, write_fault;
  	uint64_t cpu_flags;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
441
  	pte_t pte = *ptep;
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
442
  	uint64_t orig_pfn = *pfn;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
443

f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
444
  	*pfn = range->values[HMM_PFN_NONE];
73231612d   Jérôme Glisse   mm/hmm: improve a...
445
  	fault = write_fault = false;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
446
447
  
  	if (pte_none(pte)) {
73231612d   Jérôme Glisse   mm/hmm: improve a...
448
449
  		hmm_pte_need_fault(hmm_vma_walk, orig_pfn, 0,
  				   &fault, &write_fault);
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
450
  		if (fault || write_fault)
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
451
452
453
454
455
456
457
458
  			goto fault;
  		return 0;
  	}
  
  	if (!pte_present(pte)) {
  		swp_entry_t entry = pte_to_swp_entry(pte);
  
  		if (!non_swap_entry(entry)) {
e3fe8e555   Yang, Philip   mm/hmm: fix hmm_r...
459
460
461
  			cpu_flags = pte_to_hmm_pfn_flags(range, pte);
  			hmm_pte_need_fault(hmm_vma_walk, orig_pfn, cpu_flags,
  					   &fault, &write_fault);
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
462
  			if (fault || write_fault)
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
463
464
465
466
467
468
469
470
471
  				goto fault;
  			return 0;
  		}
  
  		/*
  		 * This is a special swap entry, ignore migration, use
  		 * device and report anything else as error.
  		 */
  		if (is_device_private_entry(entry)) {
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
472
473
  			cpu_flags = range->flags[HMM_PFN_VALID] |
  				range->flags[HMM_PFN_DEVICE_PRIVATE];
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
474
  			cpu_flags |= is_write_device_private_entry(entry) ?
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
475
476
477
478
479
  				range->flags[HMM_PFN_WRITE] : 0;
  			hmm_pte_need_fault(hmm_vma_walk, orig_pfn, cpu_flags,
  					   &fault, &write_fault);
  			if (fault || write_fault)
  				goto fault;
391aab11e   Jérôme Glisse   mm/hmm: convert v...
480
481
  			*pfn = hmm_device_entry_from_pfn(range,
  					    swp_offset(entry));
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
482
  			*pfn |= cpu_flags;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
483
484
485
486
  			return 0;
  		}
  
  		if (is_migration_entry(entry)) {
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
487
  			if (fault || write_fault) {
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
488
489
  				pte_unmap(ptep);
  				hmm_vma_walk->last = addr;
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
490
  				migration_entry_wait(walk->mm, pmdp, addr);
73231612d   Jérôme Glisse   mm/hmm: improve a...
491
  				return -EBUSY;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
492
493
494
495
496
  			}
  			return 0;
  		}
  
  		/* Report error for everything else */
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
497
  		*pfn = range->values[HMM_PFN_ERROR];
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
498
  		return -EFAULT;
73231612d   Jérôme Glisse   mm/hmm: improve a...
499
500
501
502
  	} else {
  		cpu_flags = pte_to_hmm_pfn_flags(range, pte);
  		hmm_pte_need_fault(hmm_vma_walk, orig_pfn, cpu_flags,
  				   &fault, &write_fault);
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
503
  	}
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
504
  	if (fault || write_fault)
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
505
  		goto fault;
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
506
507
508
509
510
511
512
513
514
  	if (pte_devmap(pte)) {
  		hmm_vma_walk->pgmap = get_dev_pagemap(pte_pfn(pte),
  					      hmm_vma_walk->pgmap);
  		if (unlikely(!hmm_vma_walk->pgmap))
  			return -EBUSY;
  	} else if (IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL) && pte_special(pte)) {
  		*pfn = range->values[HMM_PFN_SPECIAL];
  		return -EFAULT;
  	}
391aab11e   Jérôme Glisse   mm/hmm: convert v...
515
  	*pfn = hmm_device_entry_from_pfn(range, pte_pfn(pte)) | cpu_flags;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
516
517
518
  	return 0;
  
  fault:
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
519
520
521
522
  	if (hmm_vma_walk->pgmap) {
  		put_dev_pagemap(hmm_vma_walk->pgmap);
  		hmm_vma_walk->pgmap = NULL;
  	}
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
523
524
  	pte_unmap(ptep);
  	/* Fault any virtual address we were asked to fault */
2aee09d8c   Jérôme Glisse   mm/hmm: change hm...
525
  	return hmm_vma_walk_hole_(addr, end, fault, write_fault, walk);
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
526
  }
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
527
528
529
530
531
  static int hmm_vma_walk_pmd(pmd_t *pmdp,
  			    unsigned long start,
  			    unsigned long end,
  			    struct mm_walk *walk)
  {
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
532
533
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
ff05c0c6b   Jérôme Glisse   mm/hmm: use uint6...
534
  	uint64_t *pfns = range->pfns;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
535
  	unsigned long addr = start, i;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
536
  	pte_t *ptep;
d08faca01   Jérôme Glisse   mm/hmm: properly ...
537
  	pmd_t pmd;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
538

da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
539
  again:
d08faca01   Jérôme Glisse   mm/hmm: properly ...
540
541
  	pmd = READ_ONCE(*pmdp);
  	if (pmd_none(pmd))
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
542
  		return hmm_vma_walk_hole(start, end, walk);
d08faca01   Jérôme Glisse   mm/hmm: properly ...
543
544
545
546
547
548
549
550
551
552
553
554
555
  	if (thp_migration_supported() && is_pmd_migration_entry(pmd)) {
  		bool fault, write_fault;
  		unsigned long npages;
  		uint64_t *pfns;
  
  		i = (addr - range->start) >> PAGE_SHIFT;
  		npages = (end - addr) >> PAGE_SHIFT;
  		pfns = &range->pfns[i];
  
  		hmm_range_need_fault(hmm_vma_walk, pfns, npages,
  				     0, &fault, &write_fault);
  		if (fault || write_fault) {
  			hmm_vma_walk->last = addr;
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
556
  			pmd_migration_entry_wait(walk->mm, pmdp);
73231612d   Jérôme Glisse   mm/hmm: improve a...
557
  			return -EBUSY;
d08faca01   Jérôme Glisse   mm/hmm: properly ...
558
559
560
561
  		}
  		return 0;
  	} else if (!pmd_present(pmd))
  		return hmm_pfns_bad(start, end, walk);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
562

d08faca01   Jérôme Glisse   mm/hmm: properly ...
563
  	if (pmd_devmap(pmd) || pmd_trans_huge(pmd)) {
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
564
  		/*
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
565
  		 * No need to take pmd_lock here, even if some other thread
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
566
567
568
  		 * is splitting the huge pmd we will get that event through
  		 * mmu_notifier callback.
  		 *
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
569
  		 * So just read pmd value and check again it's a transparent
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
570
571
572
573
574
575
576
  		 * huge or device mapping one and compute corresponding pfn
  		 * values.
  		 */
  		pmd = pmd_read_atomic(pmdp);
  		barrier();
  		if (!pmd_devmap(pmd) && !pmd_trans_huge(pmd))
  			goto again;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
577

d08faca01   Jérôme Glisse   mm/hmm: properly ...
578
  		i = (addr - range->start) >> PAGE_SHIFT;
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
579
  		return hmm_vma_handle_pmd(walk, addr, end, &pfns[i], pmd);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
580
  	}
d08faca01   Jérôme Glisse   mm/hmm: properly ...
581
  	/*
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
582
  	 * We have handled all the valid cases above ie either none, migration,
d08faca01   Jérôme Glisse   mm/hmm: properly ...
583
584
585
586
587
  	 * huge or transparent huge. At this point either it is a valid pmd
  	 * entry pointing to pte directory or it is a bad pmd that will not
  	 * recover.
  	 */
  	if (pmd_bad(pmd))
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
588
589
590
  		return hmm_pfns_bad(start, end, walk);
  
  	ptep = pte_offset_map(pmdp, addr);
d08faca01   Jérôme Glisse   mm/hmm: properly ...
591
  	i = (addr - range->start) >> PAGE_SHIFT;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
592
  	for (; addr < end; addr += PAGE_SIZE, ptep++, i++) {
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
593
  		int r;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
594

53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
595
596
597
598
599
  		r = hmm_vma_handle_pte(walk, addr, end, pmdp, ptep, &pfns[i]);
  		if (r) {
  			/* hmm_vma_handle_pte() did unmap pte directory */
  			hmm_vma_walk->last = addr;
  			return r;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
600
  		}
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
601
  	}
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
602
603
604
605
606
607
608
609
610
611
  	if (hmm_vma_walk->pgmap) {
  		/*
  		 * We do put_dev_pagemap() here and not in hmm_vma_handle_pte()
  		 * so that we can leverage get_dev_pagemap() optimization which
  		 * will not re-take a reference on a pgmap if we already have
  		 * one.
  		 */
  		put_dev_pagemap(hmm_vma_walk->pgmap);
  		hmm_vma_walk->pgmap = NULL;
  	}
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
612
  	pte_unmap(ptep - 1);
53f5c3f48   Jérôme Glisse   mm/hmm: factor ou...
613
  	hmm_vma_walk->last = addr;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
614
615
  	return 0;
  }
f0b3c45c8   Christoph Hellwig   mm/hmm: only defi...
616
617
618
619
620
621
622
623
624
625
626
627
628
  #if defined(CONFIG_ARCH_HAS_PTE_DEVMAP) && \
      defined(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)
  static inline uint64_t pud_to_hmm_pfn_flags(struct hmm_range *range, pud_t pud)
  {
  	if (!pud_present(pud))
  		return 0;
  	return pud_write(pud) ? range->flags[HMM_PFN_VALID] |
  				range->flags[HMM_PFN_WRITE] :
  				range->flags[HMM_PFN_VALID];
  }
  
  static int hmm_vma_walk_pud(pud_t *pudp, unsigned long start, unsigned long end,
  		struct mm_walk *walk)
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
  {
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
  	unsigned long addr = start, next;
  	pmd_t *pmdp;
  	pud_t pud;
  	int ret;
  
  again:
  	pud = READ_ONCE(*pudp);
  	if (pud_none(pud))
  		return hmm_vma_walk_hole(start, end, walk);
  
  	if (pud_huge(pud) && pud_devmap(pud)) {
  		unsigned long i, npages, pfn;
  		uint64_t *pfns, cpu_flags;
  		bool fault, write_fault;
  
  		if (!pud_present(pud))
  			return hmm_vma_walk_hole(start, end, walk);
  
  		i = (addr - range->start) >> PAGE_SHIFT;
  		npages = (end - addr) >> PAGE_SHIFT;
  		pfns = &range->pfns[i];
  
  		cpu_flags = pud_to_hmm_pfn_flags(range, pud);
  		hmm_range_need_fault(hmm_vma_walk, pfns, npages,
  				     cpu_flags, &fault, &write_fault);
  		if (fault || write_fault)
  			return hmm_vma_walk_hole_(addr, end, fault,
  						write_fault, walk);
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
660
661
662
663
664
665
  		pfn = pud_pfn(pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
  		for (i = 0; i < npages; ++i, ++pfn) {
  			hmm_vma_walk->pgmap = get_dev_pagemap(pfn,
  					      hmm_vma_walk->pgmap);
  			if (unlikely(!hmm_vma_walk->pgmap))
  				return -EBUSY;
391aab11e   Jérôme Glisse   mm/hmm: convert v...
666
667
  			pfns[i] = hmm_device_entry_from_pfn(range, pfn) |
  				  cpu_flags;
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
668
669
670
671
672
673
674
  		}
  		if (hmm_vma_walk->pgmap) {
  			put_dev_pagemap(hmm_vma_walk->pgmap);
  			hmm_vma_walk->pgmap = NULL;
  		}
  		hmm_vma_walk->last = end;
  		return 0;
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
  	}
  
  	split_huge_pud(walk->vma, pudp, addr);
  	if (pud_none(*pudp))
  		goto again;
  
  	pmdp = pmd_offset(pudp, addr);
  	do {
  		next = pmd_addr_end(addr, end);
  		ret = hmm_vma_walk_pmd(pmdp, addr, next, walk);
  		if (ret)
  			return ret;
  	} while (pmdp++, addr = next, addr != end);
  
  	return 0;
  }
f0b3c45c8   Christoph Hellwig   mm/hmm: only defi...
691
692
693
  #else
  #define hmm_vma_walk_pud	NULL
  #endif
992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
694

251bbe59b   Christoph Hellwig   mm/hmm: cleanup t...
695
  #ifdef CONFIG_HUGETLB_PAGE
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
696
697
698
699
  static int hmm_vma_walk_hugetlb_entry(pte_t *pte, unsigned long hmask,
  				      unsigned long start, unsigned long end,
  				      struct mm_walk *walk)
  {
05c23af4a   Christoph Hellwig   mm/hmm: remove th...
700
  	unsigned long addr = start, i, pfn;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
701
702
703
  	struct hmm_vma_walk *hmm_vma_walk = walk->private;
  	struct hmm_range *range = hmm_vma_walk->range;
  	struct vm_area_struct *vma = walk->vma;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
704
705
706
707
708
  	uint64_t orig_pfn, cpu_flags;
  	bool fault, write_fault;
  	spinlock_t *ptl;
  	pte_t entry;
  	int ret = 0;
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
709
  	ptl = huge_pte_lock(hstate_vma(vma), walk->mm, pte);
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
710
  	entry = huge_ptep_get(pte);
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
711
  	i = (start - range->start) >> PAGE_SHIFT;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
712
713
714
715
716
717
718
719
720
721
  	orig_pfn = range->pfns[i];
  	range->pfns[i] = range->values[HMM_PFN_NONE];
  	cpu_flags = pte_to_hmm_pfn_flags(range, entry);
  	fault = write_fault = false;
  	hmm_pte_need_fault(hmm_vma_walk, orig_pfn, cpu_flags,
  			   &fault, &write_fault);
  	if (fault || write_fault) {
  		ret = -ENOENT;
  		goto unlock;
  	}
05c23af4a   Christoph Hellwig   mm/hmm: remove th...
722
  	pfn = pte_pfn(entry) + ((start & ~hmask) >> PAGE_SHIFT);
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
723
  	for (; addr < end; addr += PAGE_SIZE, i++, pfn++)
391aab11e   Jérôme Glisse   mm/hmm: convert v...
724
725
  		range->pfns[i] = hmm_device_entry_from_pfn(range, pfn) |
  				 cpu_flags;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
726
727
728
729
730
731
732
733
734
  	hmm_vma_walk->last = end;
  
  unlock:
  	spin_unlock(ptl);
  
  	if (ret == -ENOENT)
  		return hmm_vma_walk_hole_(addr, end, fault, write_fault, walk);
  
  	return ret;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
735
  }
251bbe59b   Christoph Hellwig   mm/hmm: cleanup t...
736
737
738
  #else
  #define hmm_vma_walk_hugetlb_entry NULL
  #endif /* CONFIG_HUGETLB_PAGE */
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
739

f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
740
741
  static void hmm_pfns_clear(struct hmm_range *range,
  			   uint64_t *pfns,
33cd47dcb   Jérôme Glisse   mm/hmm: move hmm_...
742
743
744
745
  			   unsigned long addr,
  			   unsigned long end)
  {
  	for (; addr < end; addr += PAGE_SIZE, pfns++)
f88a1e90c   Jérôme Glisse   mm/hmm: use devic...
746
  		*pfns = range->values[HMM_PFN_NONE];
33cd47dcb   Jérôme Glisse   mm/hmm: move hmm_...
747
  }
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
748
  /*
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
749
   * hmm_range_register() - start tracking change to CPU page table over a range
25f23a0c7   Jérôme Glisse   mm/hmm: improve a...
750
   * @range: range
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
751
   * @mm: the mm struct for the range of virtual address
fac555ac9   Christoph Hellwig   mm/hmm: remove su...
752
   *
d2e8d5511   Ralph Campbell   mm/hmm: a few mor...
753
   * Return: 0 on success, -EFAULT if the address space is no longer valid
25f23a0c7   Jérôme Glisse   mm/hmm: improve a...
754
   *
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
755
   * Track updates to the CPU page table see include/linux/hmm.h
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
756
   */
fac555ac9   Christoph Hellwig   mm/hmm: remove su...
757
  int hmm_range_register(struct hmm_range *range, struct hmm_mirror *mirror)
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
758
  {
e36acfe6c   Jason Gunthorpe   mm/hmm: Use hmm_m...
759
  	struct hmm *hmm = mirror->hmm;
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
760
  	unsigned long flags;
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
761

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
762
  	range->valid = false;
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
763
  	range->hmm = NULL;
7f08263d9   Christoph Hellwig   mm/hmm: remove th...
764
  	if ((range->start & (PAGE_SIZE - 1)) || (range->end & (PAGE_SIZE - 1)))
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
765
  		return -EINVAL;
fac555ac9   Christoph Hellwig   mm/hmm: remove su...
766
  	if (range->start >= range->end)
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
767
  		return -EINVAL;
47f245985   Jason Gunthorpe   mm/hmm: Hold on t...
768
  	/* Prevent hmm_release() from running while the range is valid */
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
769
  	if (!mmget_not_zero(hmm->mmu_notifier.mm))
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
770
  		return -EFAULT;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
771

085ea2506   Ralph Campbell   mm/hmm: clean up ...
772
  	/* Initialize range to track CPU page table updates. */
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
773
  	spin_lock_irqsave(&hmm->ranges_lock, flags);
855ce7d25   Jérôme Glisse   mm/hmm: cleanup s...
774

085ea2506   Ralph Campbell   mm/hmm: clean up ...
775
  	range->hmm = hmm;
157816f37   Jason Gunthorpe   mm/hmm: Do not us...
776
  	list_add(&range->list, &hmm->ranges);
86586a41b   Jérôme Glisse   mm/hmm: remove HM...
777

704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
778
  	/*
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
779
780
  	 * If there are any concurrent notifiers we have to wait for them for
  	 * the range to be valid (see hmm_range_wait_until_valid()).
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
781
  	 */
085ea2506   Ralph Campbell   mm/hmm: clean up ...
782
  	if (!hmm->notifiers)
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
783
  		range->valid = true;
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
784
  	spin_unlock_irqrestore(&hmm->ranges_lock, flags);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
785
786
  
  	return 0;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
787
  }
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
788
  EXPORT_SYMBOL(hmm_range_register);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
789
790
  
  /*
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
791
792
   * hmm_range_unregister() - stop tracking change to CPU page table over a range
   * @range: range
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
793
794
   *
   * Range struct is used to track updates to the CPU page table after a call to
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
795
   * hmm_range_register(). See include/linux/hmm.h for how to use it.
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
796
   */
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
797
  void hmm_range_unregister(struct hmm_range *range)
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
798
  {
085ea2506   Ralph Campbell   mm/hmm: clean up ...
799
  	struct hmm *hmm = range->hmm;
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
800
  	unsigned long flags;
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
801

5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
802
  	spin_lock_irqsave(&hmm->ranges_lock, flags);
47f245985   Jason Gunthorpe   mm/hmm: Hold on t...
803
  	list_del_init(&range->list);
5a136b4ae   Jason Gunthorpe   mm/hmm: Fix error...
804
  	spin_unlock_irqrestore(&hmm->ranges_lock, flags);
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
805

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
806
  	/* Drop reference taken by hmm_range_register() */
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
807
  	mmput(hmm->mmu_notifier.mm);
2dcc3eb8a   Jason Gunthorpe   mm/hmm: Poison hm...
808
809
810
811
812
813
  
  	/*
  	 * The range is now invalid and the ref on the hmm is dropped, so
  	 * poison the pointer.  Leave other fields in place, for the caller's
  	 * use.
  	 */
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
814
  	range->valid = false;
2dcc3eb8a   Jason Gunthorpe   mm/hmm: Poison hm...
815
  	memset(&range->hmm, POISON_INUSE, sizeof(range->hmm));
da4c3c735   Jérôme Glisse   mm/hmm/mirror: he...
816
  }
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
817
  EXPORT_SYMBOL(hmm_range_unregister);
7b86ac337   Christoph Hellwig   pagewalk: separat...
818
819
820
821
822
823
  static const struct mm_walk_ops hmm_walk_ops = {
  	.pud_entry	= hmm_vma_walk_pud,
  	.pmd_entry	= hmm_vma_walk_pmd,
  	.pte_hole	= hmm_vma_walk_hole,
  	.hugetlb_entry	= hmm_vma_walk_hugetlb_entry,
  };
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
824
825
826
827
828
829
830
831
  /**
   * hmm_range_fault - try to fault some address in a virtual address range
   * @range:	range being faulted
   * @flags:	HMM_FAULT_* flags
   *
   * Return: the number of valid pages in range->pfns[] (from range start
   * address), which may be zero.  On error one of the following status codes
   * can be returned:
73231612d   Jérôme Glisse   mm/hmm: improve a...
832
   *
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
833
834
835
836
837
838
839
840
841
842
843
   * -EINVAL:	Invalid arguments or mm or virtual address is in an invalid vma
   *		(e.g., device file vma).
   * -ENOMEM:	Out of memory.
   * -EPERM:	Invalid permission (e.g., asking for write and range is read
   *		only).
   * -EAGAIN:	A page fault needs to be retried and mmap_sem was dropped.
   * -EBUSY:	The range has been invalidated and the caller needs to wait for
   *		the invalidation to finish.
   * -EFAULT:	Invalid (i.e., either no valid vma or it is illegal to access
   *		that range) number of valid pages in range->pfns[] (from
   *              range start address).
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
844
845
   *
   * This is similar to a regular CPU page fault except that it will not trigger
73231612d   Jérôme Glisse   mm/hmm: improve a...
846
847
   * any memory migration if the memory being faulted is not accessible by CPUs
   * and caller does not ask for migration.
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
848
   *
ff05c0c6b   Jérôme Glisse   mm/hmm: use uint6...
849
850
   * On error, for one virtual address in the range, the function will mark the
   * corresponding HMM pfn entry with an error flag.
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
851
   */
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
852
  long hmm_range_fault(struct hmm_range *range, unsigned int flags)
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
853
  {
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
854
  	const unsigned long device_vma = VM_IO | VM_PFNMAP | VM_MIXEDMAP;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
855
  	unsigned long start = range->start, end;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
856
  	struct hmm_vma_walk hmm_vma_walk;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
857
858
  	struct hmm *hmm = range->hmm;
  	struct vm_area_struct *vma;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
859
  	int ret;
c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
860
  	lockdep_assert_held(&hmm->mmu_notifier.mm->mmap_sem);
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
861

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
862
863
  	do {
  		/* If range is no longer valid force retry. */
2bcbeaefd   Christoph Hellwig   mm/hmm: always re...
864
865
  		if (!range->valid)
  			return -EBUSY;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
866

c7d8b7824   Jason Gunthorpe   hmm: use mmu_noti...
867
  		vma = find_vma(hmm->mmu_notifier.mm, start);
63d5066f6   Jérôme Glisse   mm/hmm: mirror hu...
868
  		if (vma == NULL || (vma->vm_flags & device_vma))
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
869
  			return -EFAULT;
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
870

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
871
872
873
874
875
876
877
878
879
880
  		if (!(vma->vm_flags & VM_READ)) {
  			/*
  			 * If vma do not allow read access, then assume that it
  			 * does not allow write access, either. HMM does not
  			 * support architecture that allow write without read.
  			 */
  			hmm_pfns_clear(range, range->pfns,
  				range->start, range->end);
  			return -EPERM;
  		}
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
881

992de9a8b   Jérôme Glisse   mm/hmm: allow to ...
882
  		hmm_vma_walk.pgmap = NULL;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
883
  		hmm_vma_walk.last = start;
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
884
  		hmm_vma_walk.flags = flags;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
885
  		hmm_vma_walk.range = range;
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
886
  		end = min(range->end, vma->vm_end);
7b86ac337   Christoph Hellwig   pagewalk: separat...
887
888
  		walk_page_range(vma->vm_mm, start, end, &hmm_walk_ops,
  				&hmm_vma_walk);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
889
890
  
  		do {
7b86ac337   Christoph Hellwig   pagewalk: separat...
891
892
  			ret = walk_page_range(vma->vm_mm, start, end,
  					&hmm_walk_ops, &hmm_vma_walk);
a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
893
894
895
896
897
898
899
900
901
902
903
904
905
906
  			start = hmm_vma_walk.last;
  
  			/* Keep trying while the range is valid. */
  		} while (ret == -EBUSY && range->valid);
  
  		if (ret) {
  			unsigned long i;
  
  			i = (hmm_vma_walk.last - range->start) >> PAGE_SHIFT;
  			hmm_pfns_clear(range, &range->pfns[i],
  				hmm_vma_walk.last, range->end);
  			return ret;
  		}
  		start = end;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
907

a3e0d41c2   Jérôme Glisse   mm/hmm: improve d...
908
  	} while (start < range->end);
704f3f2cf   Jérôme Glisse   mm/hmm: use refer...
909

73231612d   Jérôme Glisse   mm/hmm: improve a...
910
  	return (hmm_vma_walk.last - range->start) >> PAGE_SHIFT;
74eee180b   Jérôme Glisse   mm/hmm/mirror: de...
911
  }
73231612d   Jérôme Glisse   mm/hmm: improve a...
912
  EXPORT_SYMBOL(hmm_range_fault);
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
913
914
  
  /**
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
915
916
917
918
919
   * hmm_range_dma_map - hmm_range_fault() and dma map page all in one.
   * @range:	range being faulted
   * @device:	device to map page to
   * @daddrs:	array of dma addresses for the mapped pages
   * @flags:	HMM_FAULT_*
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
920
   *
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
921
922
   * Return: the number of pages mapped on success (including zero), or any
   * status return from hmm_range_fault() otherwise.
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
923
   */
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
924
925
  long hmm_range_dma_map(struct hmm_range *range, struct device *device,
  		dma_addr_t *daddrs, unsigned int flags)
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
926
927
928
  {
  	unsigned long i, npages, mapped;
  	long ret;
9a4903e49   Christoph Hellwig   mm/hmm: replace t...
929
  	ret = hmm_range_fault(range, flags);
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
  	if (ret <= 0)
  		return ret ? ret : -EBUSY;
  
  	npages = (range->end - range->start) >> PAGE_SHIFT;
  	for (i = 0, mapped = 0; i < npages; ++i) {
  		enum dma_data_direction dir = DMA_TO_DEVICE;
  		struct page *page;
  
  		/*
  		 * FIXME need to update DMA API to provide invalid DMA address
  		 * value instead of a function to test dma address value. This
  		 * would remove lot of dumb code duplicated accross many arch.
  		 *
  		 * For now setting it to 0 here is good enough as the pfns[]
  		 * value is what is use to check what is valid and what isn't.
  		 */
  		daddrs[i] = 0;
391aab11e   Jérôme Glisse   mm/hmm: convert v...
947
  		page = hmm_device_entry_to_page(range, range->pfns[i]);
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
  		if (page == NULL)
  			continue;
  
  		/* Check if range is being invalidated */
  		if (!range->valid) {
  			ret = -EBUSY;
  			goto unmap;
  		}
  
  		/* If it is read and write than map bi-directional. */
  		if (range->pfns[i] & range->flags[HMM_PFN_WRITE])
  			dir = DMA_BIDIRECTIONAL;
  
  		daddrs[i] = dma_map_page(device, page, 0, PAGE_SIZE, dir);
  		if (dma_mapping_error(device, daddrs[i])) {
  			ret = -EFAULT;
  			goto unmap;
  		}
  
  		mapped++;
  	}
  
  	return mapped;
  
  unmap:
  	for (npages = i, i = 0; (i < npages) && mapped; ++i) {
  		enum dma_data_direction dir = DMA_TO_DEVICE;
  		struct page *page;
391aab11e   Jérôme Glisse   mm/hmm: convert v...
976
  		page = hmm_device_entry_to_page(range, range->pfns[i]);
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
  		if (page == NULL)
  			continue;
  
  		if (dma_mapping_error(device, daddrs[i]))
  			continue;
  
  		/* If it is read and write than map bi-directional. */
  		if (range->pfns[i] & range->flags[HMM_PFN_WRITE])
  			dir = DMA_BIDIRECTIONAL;
  
  		dma_unmap_page(device, daddrs[i], PAGE_SIZE, dir);
  		mapped--;
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL(hmm_range_dma_map);
  
  /**
   * hmm_range_dma_unmap() - unmap range of that was map with hmm_range_dma_map()
   * @range: range being unmapped
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
998
999
1000
   * @device: device against which dma map was done
   * @daddrs: dma address of mapped pages
   * @dirty: dirty page if it had the write flag set
085ea2506   Ralph Campbell   mm/hmm: clean up ...
1001
   * Return: number of page unmapped on success, -EINVAL otherwise
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
1002
1003
1004
1005
1006
1007
1008
   *
   * Note that caller MUST abide by mmu notifier or use HMM mirror and abide
   * to the sync_cpu_device_pagetables() callback so that it is safe here to
   * call set_page_dirty(). Caller must also take appropriate locks to avoid
   * concurrent mmu notifier or sync_cpu_device_pagetables() to make progress.
   */
  long hmm_range_dma_unmap(struct hmm_range *range,
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
  			 struct device *device,
  			 dma_addr_t *daddrs,
  			 bool dirty)
  {
  	unsigned long i, npages;
  	long cpages = 0;
  
  	/* Sanity check. */
  	if (range->end <= range->start)
  		return -EINVAL;
  	if (!daddrs)
  		return -EINVAL;
  	if (!range->pfns)
  		return -EINVAL;
  
  	npages = (range->end - range->start) >> PAGE_SHIFT;
  	for (i = 0; i < npages; ++i) {
  		enum dma_data_direction dir = DMA_TO_DEVICE;
  		struct page *page;
391aab11e   Jérôme Glisse   mm/hmm: convert v...
1028
  		page = hmm_device_entry_to_page(range, range->pfns[i]);
55c0ece82   Jérôme Glisse   mm/hmm: add a hel...
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
  		if (page == NULL)
  			continue;
  
  		/* If it is read and write than map bi-directional. */
  		if (range->pfns[i] & range->flags[HMM_PFN_WRITE]) {
  			dir = DMA_BIDIRECTIONAL;
  
  			/*
  			 * See comments in function description on why it is
  			 * safe here to call set_page_dirty()
  			 */
  			if (dirty)
  				set_page_dirty(page);
  		}
  
  		/* Unmap and clear pfns/dma address */
  		dma_unmap_page(device, daddrs[i], PAGE_SIZE, dir);
  		range->pfns[i] = range->values[HMM_PFN_NONE];
  		/* FIXME see comments in hmm_vma_dma_map() */
  		daddrs[i] = 0;
  		cpages++;
  	}
  
  	return cpages;
  }
  EXPORT_SYMBOL(hmm_range_dma_unmap);