Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2024 Intel Corporation
4 */
5
6#include <drm/ttm/ttm_backup.h>
7#include <linux/page-flags.h>
8#include <linux/swap.h>
9
10/*
11 * Casting from randomized struct file * to struct ttm_backup * is fine since
12 * struct ttm_backup is never defined nor dereferenced.
13 */
14static struct file *ttm_backup_to_file(struct ttm_backup *backup)
15{
16 return (void *)backup;
17}
18
19static struct ttm_backup *ttm_file_to_backup(struct file *file)
20{
21 return (void *)file;
22}
23
24/*
25 * Need to map shmem indices to handle since a handle value
26 * of 0 means error, following the swp_entry_t convention.
27 */
28static unsigned long ttm_backup_shmem_idx_to_handle(pgoff_t idx)
29{
30 return (unsigned long)idx + 1;
31}
32
33static pgoff_t ttm_backup_handle_to_shmem_idx(pgoff_t handle)
34{
35 return handle - 1;
36}
37
38/**
39 * ttm_backup_drop() - release memory associated with a handle
40 * @backup: The struct backup pointer used to obtain the handle
41 * @handle: The handle obtained from the @backup_page function.
42 */
43void ttm_backup_drop(struct ttm_backup *backup, pgoff_t handle)
44{
45 loff_t start = ttm_backup_handle_to_shmem_idx(handle);
46
47 start <<= PAGE_SHIFT;
48 shmem_truncate_range(file_inode(ttm_backup_to_file(backup)), start,
49 start + PAGE_SIZE - 1);
50}
51
52/**
53 * ttm_backup_copy_page() - Copy the contents of a previously backed
54 * up page
55 * @backup: The struct backup pointer used to back up the page.
56 * @dst: The struct page to copy into.
57 * @handle: The handle returned when the page was backed up.
58 * @intr: Try to perform waits interruptable or at least killable.
59 *
60 * Return: 0 on success, Negative error code on failure, notably
61 * -EINTR if @intr was set to true and a signal is pending.
62 */
63int ttm_backup_copy_page(struct ttm_backup *backup, struct page *dst,
64 pgoff_t handle, bool intr)
65{
66 struct file *filp = ttm_backup_to_file(backup);
67 struct address_space *mapping = filp->f_mapping;
68 struct folio *from_folio;
69 pgoff_t idx = ttm_backup_handle_to_shmem_idx(handle);
70
71 from_folio = shmem_read_folio(mapping, idx);
72 if (IS_ERR(from_folio))
73 return PTR_ERR(from_folio);
74
75 copy_highpage(dst, folio_file_page(from_folio, idx));
76 folio_put(from_folio);
77
78 return 0;
79}
80
81/**
82 * ttm_backup_backup_page() - Backup a page
83 * @backup: The struct backup pointer to use.
84 * @page: The page to back up.
85 * @writeback: Whether to perform immediate writeback of the page.
86 * This may have performance implications.
87 * @idx: A unique integer for each page and each struct backup.
88 * This allows the backup implementation to avoid managing
89 * its address space separately.
90 * @page_gfp: The gfp value used when the page was allocated.
91 * This is used for accounting purposes.
92 * @alloc_gfp: The gfp to be used when allocating memory.
93 *
94 * Context: If called from reclaim context, the caller needs to
95 * assert that the shrinker gfp has __GFP_FS set, to avoid
96 * deadlocking on lock_page(). If @writeback is set to true and
97 * called from reclaim context, the caller also needs to assert
98 * that the shrinker gfp has __GFP_IO set, since without it,
99 * we're not allowed to start backup IO.
100 *
101 * Return: A handle on success. Negative error code on failure.
102 *
103 * Note: This function could be extended to back up a folio and
104 * implementations would then split the folio internally if needed.
105 * Drawback is that the caller would then have to keep track of
106 * the folio size- and usage.
107 */
108s64
109ttm_backup_backup_page(struct ttm_backup *backup, struct page *page,
110 bool writeback, pgoff_t idx, gfp_t page_gfp,
111 gfp_t alloc_gfp)
112{
113 struct file *filp = ttm_backup_to_file(backup);
114 struct address_space *mapping = filp->f_mapping;
115 unsigned long handle = 0;
116 struct folio *to_folio;
117 int ret;
118
119 to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
120 if (IS_ERR(to_folio))
121 return PTR_ERR(to_folio);
122
123 folio_mark_accessed(to_folio);
124 folio_lock(to_folio);
125 folio_mark_dirty(to_folio);
126 copy_highpage(folio_file_page(to_folio, idx), page);
127 handle = ttm_backup_shmem_idx_to_handle(idx);
128
129 if (writeback && !folio_mapped(to_folio) &&
130 folio_clear_dirty_for_io(to_folio)) {
131 struct writeback_control wbc = {
132 .sync_mode = WB_SYNC_NONE,
133 .nr_to_write = SWAP_CLUSTER_MAX,
134 .range_start = 0,
135 .range_end = LLONG_MAX,
136 .for_reclaim = 1,
137 };
138 folio_set_reclaim(to_folio);
139 ret = mapping->a_ops->writepage(folio_file_page(to_folio, idx), &wbc);
140 if (!folio_test_writeback(to_folio))
141 folio_clear_reclaim(to_folio);
142 /*
143 * If writepage succeeds, it unlocks the folio.
144 * writepage() errors are otherwise dropped, since writepage()
145 * is only best effort here.
146 */
147 if (ret)
148 folio_unlock(to_folio);
149 } else {
150 folio_unlock(to_folio);
151 }
152
153 folio_put(to_folio);
154
155 return handle;
156}
157
158/**
159 * ttm_backup_fini() - Free the struct backup resources after last use.
160 * @backup: Pointer to the struct backup whose resources to free.
161 *
162 * After a call to this function, it's illegal to use the @backup pointer.
163 */
164void ttm_backup_fini(struct ttm_backup *backup)
165{
166 fput(ttm_backup_to_file(backup));
167}
168
169/**
170 * ttm_backup_bytes_avail() - Report the approximate number of bytes of backup space
171 * left for backup.
172 *
173 * This function is intended also for driver use to indicate whether a
174 * backup attempt is meaningful.
175 *
176 * Return: An approximate size of backup space available.
177 */
178u64 ttm_backup_bytes_avail(void)
179{
180 /*
181 * The idea behind backing up to shmem is that shmem objects may
182 * eventually be swapped out. So no point swapping out if there
183 * is no or low swap-space available. But the accuracy of this
184 * number also depends on shmem actually swapping out backed-up
185 * shmem objects without too much buffering.
186 */
187 return (u64)get_nr_swap_pages() << PAGE_SHIFT;
188}
189EXPORT_SYMBOL_GPL(ttm_backup_bytes_avail);
190
191/**
192 * ttm_backup_shmem_create() - Create a shmem-based struct backup.
193 * @size: The maximum size (in bytes) to back up.
194 *
195 * Create a backup utilizing shmem objects.
196 *
197 * Return: A pointer to a struct ttm_backup on success,
198 * an error pointer on error.
199 */
200struct ttm_backup *ttm_backup_shmem_create(loff_t size)
201{
202 struct file *filp;
203
204 filp = shmem_file_setup("ttm shmem backup", size, 0);
205
206 return ttm_file_to_backup(filp);
207}