]> git.saurik.com Git - apple/xnu.git/blob - bsd/vm/vm_compressor_backing_file.c
9a663e9e848e7e5f821f778cff6dec569a1afba1
[apple/xnu.git] / bsd / vm / vm_compressor_backing_file.c
1 /*
2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <stdint.h>
30 #include <sys/fcntl.h>
31 #include <sys/vnode_internal.h>
32 #include <sys/vnode.h>
33 #include <sys/kauth.h>
34 #include <sys/mount_internal.h>
35 #include <sys/buf_internal.h>
36 #include <kern/debug.h>
37 #include <kern/kalloc.h>
38 #include <sys/cprotect.h>
39 #include <sys/disk.h>
40 #include <vm/vm_protos.h>
41 #include <vm/vm_pageout.h>
42
43 void vm_swapfile_open(const char *path, vnode_t *vp);
44 void vm_swapfile_close(uint64_t path, vnode_t vp);
45 int vm_swapfile_preallocate(vnode_t vp, uint64_t *size);
46 uint64_t vm_swapfile_get_blksize(vnode_t vp);
47 uint64_t vm_swapfile_get_transfer_size(vnode_t vp);
48 int vm_swapfile_io(vnode_t vp, uint64_t offset, uint64_t start, int npages, int flags);
49
50 void
51 vm_swapfile_open(const char *path, vnode_t *vp)
52 {
53 int error = 0;
54 vfs_context_t ctx = vfs_context_current();
55
56 if ((error = vnode_open(path, (O_CREAT | O_TRUNC | FREAD | FWRITE), S_IRUSR | S_IWUSR, 0, vp, ctx))) {
57 printf("Failed to open swap file %d\n", error);
58 *vp = NULL;
59 return;
60 }
61
62 vnode_put(*vp);
63 }
64
65 uint64_t
66 vm_swapfile_get_blksize(vnode_t vp)
67 {
68 return ((uint64_t)vfs_devblocksize(vnode_mount(vp)));
69 }
70
71 uint64_t
72 vm_swapfile_get_transfer_size(vnode_t vp)
73 {
74 return((uint64_t)vp->v_mount->mnt_vfsstat.f_iosize);
75 }
76
77 int unlink1(vfs_context_t, vnode_t, user_addr_t, enum uio_seg, int);
78
79 void
80 vm_swapfile_close(uint64_t path_addr, vnode_t vp)
81 {
82 vfs_context_t context = vfs_context_current();
83 int error;
84
85 vnode_getwithref(vp);
86 vnode_close(vp, 0, context);
87
88 error = unlink1(context, NULLVP, CAST_USER_ADDR_T(path_addr),
89 UIO_SYSSPACE, 0);
90
91 #if DEVELOPMENT || DEBUG
92 if (error)
93 printf("%s : unlink of %s failed with error %d", __FUNCTION__,
94 (char *)path_addr, error);
95 #endif
96 }
97
98 int
99 vm_swapfile_preallocate(vnode_t vp, uint64_t *size)
100 {
101 int error = 0;
102 uint64_t file_size = 0;
103 vfs_context_t ctx = NULL;
104
105
106 ctx = vfs_context_current();
107
108 #if CONFIG_PROTECT
109 {
110 #if 0 // <rdar://11771612>
111
112 if ((error = cp_vnode_setclass(vp, PROTECTION_CLASS_F))) {
113 if(config_protect_bug) {
114 printf("swap protection class set failed with %d\n", error);
115 } else {
116 panic("swap protection class set failed with %d\n", error);
117 }
118 }
119 #endif
120 /* initialize content protection keys manually */
121 if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) {
122 printf("Content Protection key failure on swap: %d\n", error);
123 vnode_put(vp);
124 vp = NULL;
125 goto done;
126 }
127 }
128 #endif
129
130 error = vnode_setsize(vp, *size, IO_NOZEROFILL, ctx);
131
132 if (error) {
133 printf("vnode_setsize for swap files failed: %d\n", error);
134 goto done;
135 }
136
137 error = vnode_size(vp, (off_t*) &file_size, ctx);
138
139 if (error) {
140 printf("vnode_size (new file) for swap file failed: %d\n", error);
141 }
142
143 assert(file_size == *size);
144
145 vnode_lock_spin(vp);
146 SET(vp->v_flag, VSWAP);
147 vnode_unlock(vp);
148 done:
149 return error;
150 }
151
152 int
153 vm_swapfile_io(vnode_t vp, uint64_t offset, uint64_t start, int npages, int flags)
154 {
155 int error = 0;
156 uint64_t io_size = npages * PAGE_SIZE_64;
157 #if 1
158 kern_return_t kr = KERN_SUCCESS;
159 upl_t upl = NULL;
160 unsigned int count = 0;
161 int upl_create_flags = 0, upl_control_flags = 0;
162 upl_size_t upl_size = 0;
163
164 upl_create_flags = UPL_SET_INTERNAL | UPL_SET_LITE;
165
166 #if ENCRYPTED_SWAP
167 upl_control_flags = UPL_IOSYNC | UPL_PAGING_ENCRYPTED;
168 #else
169 upl_control_flags = UPL_IOSYNC;
170 #endif
171 if ((flags & SWAP_READ) == FALSE) {
172 upl_create_flags |= UPL_COPYOUT_FROM;
173 }
174
175 upl_size = io_size;
176 kr = vm_map_create_upl( kernel_map,
177 start,
178 &upl_size,
179 &upl,
180 NULL,
181 &count,
182 &upl_create_flags);
183
184 if (kr != KERN_SUCCESS || (upl_size != io_size)) {
185 panic("vm_map_create_upl failed with %d\n", kr);
186 }
187
188 if (flags & SWAP_READ) {
189 vnode_pagein(vp,
190 upl,
191 0,
192 offset,
193 io_size,
194 upl_control_flags | UPL_IGNORE_VALID_PAGE_CHECK,
195 &error);
196 if (error) {
197 #if DEBUG
198 printf("vm_swapfile_io: vnode_pagein failed with %d (vp: %p, offset: 0x%llx, size:%llu)\n", error, vp, offset, io_size);
199 #else /* DEBUG */
200 printf("vm_swapfile_io: vnode_pagein failed with %d.\n", error);
201 #endif /* DEBUG */
202 }
203
204 } else {
205 vnode_pageout(vp,
206 upl,
207 0,
208 offset,
209 io_size,
210 upl_control_flags,
211 &error);
212 if (error) {
213 #if DEBUG
214 printf("vm_swapfile_io: vnode_pageout failed with %d (vp: %p, offset: 0x%llx, size:%llu)\n", error, vp, offset, io_size);
215 #else /* DEBUG */
216 printf("vm_swapfile_io: vnode_pageout failed with %d.\n", error);
217 #endif /* DEBUG */
218 }
219 }
220 return error;
221
222 #else /* 1 */
223 vfs_context_t ctx;
224 ctx = vfs_context_kernel();
225
226 error = vn_rdwr((flags & SWAP_READ) ? UIO_READ : UIO_WRITE, vp, (caddr_t)start, io_size, offset,
227 UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED | IO_UNIT | IO_NOCACHE | IO_SWAP_DISPATCH, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx));
228
229 if (error) {
230 printf("vn_rdwr: Swap I/O failed with %d\n", error);
231 }
232 return error;
233 #endif /* 1 */
234 }
235
236
237 #define MAX_BATCH_TO_TRIM 256
238
239 #define ROUTE_ONLY 0x10 /* if corestorage is present, tell it to just pass */
240 /* the DKIOUNMAP command through w/o acting on it */
241 /* this is used by the compressed swap system to reclaim empty space */
242
243
244 u_int32_t vnode_trim_list (vnode_t vp, struct trim_list *tl, boolean_t route_only)
245 {
246 int error = 0;
247 int trim_index = 0;
248 u_int32_t blocksize = 0;
249 struct vnode *devvp;
250 dk_extent_t *extents;
251 dk_unmap_t unmap;
252 _dk_cs_unmap_t cs_unmap;
253
254 if ( !(vp->v_mount->mnt_ioflags & MNT_IOFLAGS_UNMAP_SUPPORTED))
255 return (ENOTSUP);
256
257 if (tl == NULL)
258 return (0);
259
260 /*
261 * Get the underlying device vnode and physical block size
262 */
263 devvp = vp->v_mount->mnt_devvp;
264 blocksize = vp->v_mount->mnt_devblocksize;
265
266 extents = kalloc(sizeof(dk_extent_t) * MAX_BATCH_TO_TRIM);
267
268 if (vp->v_mount->mnt_ioflags & MNT_IOFLAGS_CSUNMAP_SUPPORTED) {
269 memset (&cs_unmap, 0, sizeof(_dk_cs_unmap_t));
270 cs_unmap.extents = extents;
271
272 if (route_only == TRUE)
273 cs_unmap.options = ROUTE_ONLY;
274 } else {
275 memset (&unmap, 0, sizeof(dk_unmap_t));
276 unmap.extents = extents;
277 }
278
279 while (tl) {
280 daddr64_t io_blockno; /* Block number corresponding to the start of the extent */
281 size_t io_bytecount; /* Number of bytes in current extent for the specified range */
282 size_t trimmed;
283 size_t remaining_length;
284 off_t current_offset;
285
286 current_offset = tl->tl_offset;
287 remaining_length = tl->tl_length;
288 trimmed = 0;
289
290 /*
291 * We may not get the entire range from tl_offset -> tl_offset+tl_length in a single
292 * extent from the blockmap call. Keep looping/going until we are sure we've hit
293 * the whole range or if we encounter an error.
294 */
295 while (trimmed < tl->tl_length) {
296 /*
297 * VNOP_BLOCKMAP will tell us the logical to physical block number mapping for the
298 * specified offset. It returns blocks in contiguous chunks, so if the logical range is
299 * broken into multiple extents, it must be called multiple times, increasing the offset
300 * in each call to ensure that the entire range is covered.
301 */
302 error = VNOP_BLOCKMAP (vp, current_offset, remaining_length,
303 &io_blockno, &io_bytecount, NULL, VNODE_READ, NULL);
304
305 if (error) {
306 goto trim_exit;
307 }
308
309 extents[trim_index].offset = (uint64_t) io_blockno * (u_int64_t) blocksize;
310 extents[trim_index].length = io_bytecount;
311
312 trim_index++;
313
314 if (trim_index == MAX_BATCH_TO_TRIM) {
315
316 if (vp->v_mount->mnt_ioflags & MNT_IOFLAGS_CSUNMAP_SUPPORTED) {
317 cs_unmap.extentsCount = trim_index;
318 error = VNOP_IOCTL(devvp, _DKIOCCSUNMAP, (caddr_t)&cs_unmap, 0, vfs_context_kernel());
319 } else {
320 unmap.extentsCount = trim_index;
321 error = VNOP_IOCTL(devvp, DKIOCUNMAP, (caddr_t)&unmap, 0, vfs_context_kernel());
322 }
323 if (error) {
324 goto trim_exit;
325 }
326 trim_index = 0;
327 }
328 trimmed += io_bytecount;
329 current_offset += io_bytecount;
330 remaining_length -= io_bytecount;
331 }
332 tl = tl->tl_next;
333 }
334 if (trim_index) {
335 if (vp->v_mount->mnt_ioflags & MNT_IOFLAGS_CSUNMAP_SUPPORTED) {
336 cs_unmap.extentsCount = trim_index;
337 error = VNOP_IOCTL(devvp, _DKIOCCSUNMAP, (caddr_t)&cs_unmap, 0, vfs_context_kernel());
338 } else {
339 unmap.extentsCount = trim_index;
340 error = VNOP_IOCTL(devvp, DKIOCUNMAP, (caddr_t)&unmap, 0, vfs_context_kernel());
341 }
342 }
343 trim_exit:
344 kfree(extents, sizeof(dk_extent_t) * MAX_BATCH_TO_TRIM);
345
346 return error;
347 }