]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/module/os/freebsd/zfs/vdev_file.c
MFV 2.0-rc2
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / module / os / freebsd / zfs / vdev_file.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
24  */
25
26 #include <sys/zfs_context.h>
27 #include <sys/spa.h>
28 #include <sys/file.h>
29 #include <sys/vdev_file.h>
30 #include <sys/vdev_impl.h>
31 #include <sys/zio.h>
32 #include <sys/fs/zfs.h>
33 #include <sys/fm/fs/zfs.h>
34 #include <sys/abd.h>
35 #include <sys/stat.h>
36
37 /*
38  * Virtual device vector for files.
39  */
40
41 static taskq_t *vdev_file_taskq;
42
43 unsigned long vdev_file_logical_ashift = SPA_MINBLOCKSHIFT;
44 unsigned long vdev_file_physical_ashift = SPA_MINBLOCKSHIFT;
45
46 void
47 vdev_file_init(void)
48 {
49         vdev_file_taskq = taskq_create("z_vdev_file", MAX(max_ncpus, 16),
50             minclsyspri, max_ncpus, INT_MAX, 0);
51 }
52
53 void
54 vdev_file_fini(void)
55 {
56         taskq_destroy(vdev_file_taskq);
57 }
58
59 static void
60 vdev_file_hold(vdev_t *vd)
61 {
62         ASSERT(vd->vdev_path != NULL);
63 }
64
65 static void
66 vdev_file_rele(vdev_t *vd)
67 {
68         ASSERT(vd->vdev_path != NULL);
69 }
70
71 static mode_t
72 vdev_file_open_mode(spa_mode_t spa_mode)
73 {
74         mode_t mode = 0;
75
76         if ((spa_mode & SPA_MODE_READ) && (spa_mode & SPA_MODE_WRITE)) {
77                 mode = O_RDWR;
78         } else if (spa_mode & SPA_MODE_READ) {
79                 mode = O_RDONLY;
80         } else if (spa_mode & SPA_MODE_WRITE) {
81                 mode = O_WRONLY;
82         }
83
84         return (mode | O_LARGEFILE);
85 }
86
87 static int
88 vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
89     uint64_t *logical_ashift, uint64_t *physical_ashift)
90 {
91         vdev_file_t *vf;
92         zfs_file_t *fp;
93         zfs_file_attr_t zfa;
94         int error;
95
96         /*
97          * Rotational optimizations only make sense on block devices.
98          */
99         vd->vdev_nonrot = B_TRUE;
100
101         /*
102          * Allow TRIM on file based vdevs.  This may not always be supported,
103          * since it depends on your kernel version and underlying filesystem
104          * type but it is always safe to attempt.
105          */
106         vd->vdev_has_trim = B_TRUE;
107
108         /*
109          * Disable secure TRIM on file based vdevs.  There is no way to
110          * request this behavior from the underlying filesystem.
111          */
112         vd->vdev_has_securetrim = B_FALSE;
113
114         /*
115          * We must have a pathname, and it must be absolute.
116          */
117         if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
118                 vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
119                 return (SET_ERROR(EINVAL));
120         }
121
122         /*
123          * Reopen the device if it's not currently open.  Otherwise,
124          * just update the physical size of the device.
125          */
126         if (vd->vdev_tsd != NULL) {
127                 ASSERT(vd->vdev_reopening);
128                 vf = vd->vdev_tsd;
129                 goto skip_open;
130         }
131
132         vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP);
133
134         /*
135          * We always open the files from the root of the global zone, even if
136          * we're in a local zone.  If the user has gotten to this point, the
137          * administrator has already decided that the pool should be available
138          * to local zone users, so the underlying devices should be as well.
139          */
140         ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/');
141
142         error = zfs_file_open(vd->vdev_path,
143             vdev_file_open_mode(spa_mode(vd->vdev_spa)), 0, &fp);
144         if (error) {
145                 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
146                 return (error);
147         }
148
149         vf->vf_file = fp;
150
151 #ifdef _KERNEL
152         /*
153          * Make sure it's a regular file.
154          */
155         if (zfs_file_getattr(fp, &zfa)) {
156                 return (SET_ERROR(ENODEV));
157         }
158         if (!S_ISREG(zfa.zfa_mode)) {
159                 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
160                 return (SET_ERROR(ENODEV));
161         }
162 #endif
163
164 skip_open:
165
166         error =  zfs_file_getattr(vf->vf_file, &zfa);
167         if (error) {
168                 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
169                 return (error);
170         }
171
172         *max_psize = *psize = zfa.zfa_size;
173         *logical_ashift = vdev_file_logical_ashift;
174         *physical_ashift = vdev_file_physical_ashift;
175
176         return (0);
177 }
178
179 static void
180 vdev_file_close(vdev_t *vd)
181 {
182         vdev_file_t *vf = vd->vdev_tsd;
183
184         if (vd->vdev_reopening || vf == NULL)
185                 return;
186
187         if (vf->vf_file != NULL) {
188                 zfs_file_close(vf->vf_file);
189         }
190
191         vd->vdev_delayed_close = B_FALSE;
192         kmem_free(vf, sizeof (vdev_file_t));
193         vd->vdev_tsd = NULL;
194 }
195
196 /*
197  * Implements the interrupt side for file vdev types. This routine will be
198  * called when the I/O completes allowing us to transfer the I/O to the
199  * interrupt taskqs. For consistency, the code structure mimics disk vdev
200  * types.
201  */
202 static void
203 vdev_file_io_intr(zio_t *zio)
204 {
205         zio_delay_interrupt(zio);
206 }
207
208 static void
209 vdev_file_io_strategy(void *arg)
210 {
211         zio_t *zio = arg;
212         vdev_t *vd = zio->io_vd;
213         vdev_file_t *vf;
214         void *buf;
215         ssize_t resid;
216         loff_t off;
217         ssize_t size;
218         int err;
219
220         off = zio->io_offset;
221         size = zio->io_size;
222         resid = 0;
223
224         vf = vd->vdev_tsd;
225
226         ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE);
227         if (zio->io_type == ZIO_TYPE_READ) {
228                 buf = abd_borrow_buf(zio->io_abd, zio->io_size);
229                 err = zfs_file_pread(vf->vf_file, buf, size, off, &resid);
230                 abd_return_buf_copy(zio->io_abd, buf, size);
231         } else {
232                 buf = abd_borrow_buf_copy(zio->io_abd, zio->io_size);
233                 err = zfs_file_pwrite(vf->vf_file, buf, size, off, &resid);
234                 abd_return_buf(zio->io_abd, buf, size);
235         }
236         if (resid != 0 && zio->io_error == 0)
237                 zio->io_error = ENOSPC;
238
239         vdev_file_io_intr(zio);
240 }
241
242 static void
243 vdev_file_io_start(zio_t *zio)
244 {
245         vdev_t *vd = zio->io_vd;
246         vdev_file_t *vf = vd->vdev_tsd;
247
248         if (zio->io_type == ZIO_TYPE_IOCTL) {
249                 /* XXPOLICY */
250                 if (!vdev_readable(vd)) {
251                         zio->io_error = SET_ERROR(ENXIO);
252                         zio_interrupt(zio);
253                         return;
254                 }
255
256                 switch (zio->io_cmd) {
257                 case DKIOCFLUSHWRITECACHE:
258                         zio->io_error = zfs_file_fsync(vf->vf_file,
259                             O_SYNC|O_DSYNC);
260                         break;
261                 default:
262                         zio->io_error = SET_ERROR(ENOTSUP);
263                 }
264
265                 zio_execute(zio);
266                 return;
267         } else if (zio->io_type == ZIO_TYPE_TRIM) {
268 #ifdef notyet
269                 int mode = 0;
270
271                 ASSERT3U(zio->io_size, !=, 0);
272
273                 /* XXX FreeBSD has no fallocate routine in file ops */
274                 zio->io_error = zfs_file_fallocate(vf->vf_file,
275                     mode, zio->io_offset, zio->io_size);
276 #endif
277                 zio->io_error = SET_ERROR(ENOTSUP);
278                 zio_execute(zio);
279                 return;
280         }
281         ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE);
282         zio->io_target_timestamp = zio_handle_io_delay(zio);
283
284         VERIFY3U(taskq_dispatch(vdev_file_taskq, vdev_file_io_strategy, zio,
285             TQ_SLEEP), !=, 0);
286 }
287
288 /* ARGSUSED */
289 static void
290 vdev_file_io_done(zio_t *zio)
291 {
292 }
293
294 vdev_ops_t vdev_file_ops = {
295         vdev_file_open,
296         vdev_file_close,
297         vdev_default_asize,
298         vdev_file_io_start,
299         vdev_file_io_done,
300         NULL,
301         NULL,
302         vdev_file_hold,
303         vdev_file_rele,
304         NULL,
305         vdev_default_xlate,
306         VDEV_TYPE_FILE,         /* name of this vdev type */
307         B_TRUE                  /* leaf vdev */
308 };
309
310 /*
311  * From userland we access disks just like files.
312  */
313 #ifndef _KERNEL
314
315 vdev_ops_t vdev_disk_ops = {
316         vdev_file_open,
317         vdev_file_close,
318         vdev_default_asize,
319         vdev_file_io_start,
320         vdev_file_io_done,
321         NULL,
322         NULL,
323         vdev_file_hold,
324         vdev_file_rele,
325         NULL,
326         vdev_default_xlate,
327         VDEV_TYPE_DISK,         /* name of this vdev type */
328         B_TRUE                  /* leaf vdev */
329 };
330
331 #endif
332
333 ZFS_MODULE_PARAM(zfs_vdev_file, vdev_file_, logical_ashift, ULONG, ZMOD_RW,
334         "Logical ashift for file-based devices");
335 ZFS_MODULE_PARAM(zfs_vdev_file, vdev_file_, physical_ashift, ULONG, ZMOD_RW,
336         "Physical ashift for file-based devices");