~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/xfs/xfs_buf_mem.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
  4  * Author: Darrick J. Wong <djwong@kernel.org>
  5  */
  6 #include "xfs.h"
  7 #include "xfs_fs.h"
  8 #include "xfs_buf.h"
  9 #include "xfs_buf_mem.h"
 10 #include "xfs_trace.h"
 11 #include <linux/shmem_fs.h>
 12 #include "xfs_log_format.h"
 13 #include "xfs_trans.h"
 14 #include "xfs_buf_item.h"
 15 #include "xfs_error.h"
 16 
 17 /*
 18  * Buffer Cache for In-Memory Files
 19  * ================================
 20  *
 21  * Online fsck wants to create ephemeral ordered recordsets.  The existing
 22  * btree infrastructure can do this, but we need the buffer cache to target
 23  * memory instead of block devices.
 24  *
 25  * When CONFIG_TMPFS=y, shmemfs is enough of a filesystem to meet those
 26  * requirements.  Therefore, the xmbuf mechanism uses an unlinked shmem file to
 27  * store our staging data.  This file is not installed in the file descriptor
 28  * table so that user programs cannot access the data, which means that the
 29  * xmbuf must be freed with xmbuf_destroy.
 30  *
 31  * xmbufs assume that the caller will handle all required concurrency
 32  * management; standard vfs locks (freezer and inode) are not taken.  Reads
 33  * and writes are satisfied directly from the page cache.
 34  *
 35  * The only supported block size is PAGE_SIZE, and we cannot use highmem.
 36  */
 37 
 38 /*
 39  * shmem files used to back an in-memory buffer cache must not be exposed to
 40  * userspace.  Upper layers must coordinate access to the one handle returned
 41  * by the constructor, so establish a separate lock class for xmbufs to avoid
 42  * confusing lockdep.
 43  */
 44 static struct lock_class_key xmbuf_i_mutex_key;
 45 
 46 /*
 47  * Allocate a buffer cache target for a memory-backed file and set up the
 48  * buffer target.
 49  */
 50 int
 51 xmbuf_alloc(
 52         struct xfs_mount        *mp,
 53         const char              *descr,
 54         struct xfs_buftarg      **btpp)
 55 {
 56         struct file             *file;
 57         struct inode            *inode;
 58         struct xfs_buftarg      *btp;
 59         int                     error;
 60 
 61         btp = kzalloc(struct_size(btp, bt_cache, 1), GFP_KERNEL);
 62         if (!btp)
 63                 return -ENOMEM;
 64 
 65         file = shmem_kernel_file_setup(descr, 0, 0);
 66         if (IS_ERR(file)) {
 67                 error = PTR_ERR(file);
 68                 goto out_free_btp;
 69         }
 70         inode = file_inode(file);
 71 
 72         /* private file, private locking */
 73         lockdep_set_class(&inode->i_rwsem, &xmbuf_i_mutex_key);
 74 
 75         /*
 76          * We don't want to bother with kmapping data during repair, so don't
 77          * allow highmem pages to back this mapping.
 78          */
 79         mapping_set_gfp_mask(inode->i_mapping, GFP_KERNEL);
 80 
 81         /* ensure all writes are below EOF to avoid pagecache zeroing */
 82         i_size_write(inode, inode->i_sb->s_maxbytes);
 83 
 84         error = xfs_buf_cache_init(btp->bt_cache);
 85         if (error)
 86                 goto out_file;
 87 
 88         /* Initialize buffer target */
 89         btp->bt_mount = mp;
 90         btp->bt_dev = (dev_t)-1U;
 91         btp->bt_bdev = NULL; /* in-memory buftargs have no bdev */
 92         btp->bt_file = file;
 93         btp->bt_meta_sectorsize = XMBUF_BLOCKSIZE;
 94         btp->bt_meta_sectormask = XMBUF_BLOCKSIZE - 1;
 95 
 96         error = xfs_init_buftarg(btp, XMBUF_BLOCKSIZE, descr);
 97         if (error)
 98                 goto out_bcache;
 99 
100         trace_xmbuf_create(btp);
101 
102         *btpp = btp;
103         return 0;
104 
105 out_bcache:
106         xfs_buf_cache_destroy(btp->bt_cache);
107 out_file:
108         fput(file);
109 out_free_btp:
110         kfree(btp);
111         return error;
112 }
113 
114 /* Free a buffer cache target for a memory-backed buffer cache. */
115 void
116 xmbuf_free(
117         struct xfs_buftarg      *btp)
118 {
119         ASSERT(xfs_buftarg_is_mem(btp));
120         ASSERT(percpu_counter_sum(&btp->bt_io_count) == 0);
121 
122         trace_xmbuf_free(btp);
123 
124         xfs_destroy_buftarg(btp);
125         xfs_buf_cache_destroy(btp->bt_cache);
126         fput(btp->bt_file);
127         kfree(btp);
128 }
129 
130 /* Directly map a shmem page into the buffer cache. */
131 int
132 xmbuf_map_page(
133         struct xfs_buf          *bp)
134 {
135         struct inode            *inode = file_inode(bp->b_target->bt_file);
136         struct folio            *folio = NULL;
137         struct page             *page;
138         loff_t                  pos = BBTOB(xfs_buf_daddr(bp));
139         int                     error;
140 
141         ASSERT(xfs_buftarg_is_mem(bp->b_target));
142 
143         if (bp->b_map_count != 1)
144                 return -ENOMEM;
145         if (BBTOB(bp->b_length) != XMBUF_BLOCKSIZE)
146                 return -ENOMEM;
147         if (offset_in_page(pos) != 0) {
148                 ASSERT(offset_in_page(pos));
149                 return -ENOMEM;
150         }
151 
152         error = shmem_get_folio(inode, pos >> PAGE_SHIFT, &folio, SGP_CACHE);
153         if (error)
154                 return error;
155 
156         if (filemap_check_wb_err(inode->i_mapping, 0)) {
157                 folio_unlock(folio);
158                 folio_put(folio);
159                 return -EIO;
160         }
161 
162         page = folio_file_page(folio, pos >> PAGE_SHIFT);
163 
164         /*
165          * Mark the page dirty so that it won't be reclaimed once we drop the
166          * (potentially last) reference in xmbuf_unmap_page.
167          */
168         set_page_dirty(page);
169         unlock_page(page);
170 
171         bp->b_addr = page_address(page);
172         bp->b_pages = bp->b_page_array;
173         bp->b_pages[0] = page;
174         bp->b_page_count = 1;
175         return 0;
176 }
177 
178 /* Unmap a shmem page that was mapped into the buffer cache. */
179 void
180 xmbuf_unmap_page(
181         struct xfs_buf          *bp)
182 {
183         struct page             *page = bp->b_pages[0];
184 
185         ASSERT(xfs_buftarg_is_mem(bp->b_target));
186 
187         put_page(page);
188 
189         bp->b_addr = NULL;
190         bp->b_pages[0] = NULL;
191         bp->b_pages = NULL;
192         bp->b_page_count = 0;
193 }
194 
195 /* Is this a valid daddr within the buftarg? */
196 bool
197 xmbuf_verify_daddr(
198         struct xfs_buftarg      *btp,
199         xfs_daddr_t             daddr)
200 {
201         struct inode            *inode = file_inode(btp->bt_file);
202 
203         ASSERT(xfs_buftarg_is_mem(btp));
204 
205         return daddr < (inode->i_sb->s_maxbytes >> BBSHIFT);
206 }
207 
208 /* Discard the page backing this buffer. */
209 static void
210 xmbuf_stale(
211         struct xfs_buf          *bp)
212 {
213         struct inode            *inode = file_inode(bp->b_target->bt_file);
214         loff_t                  pos;
215 
216         ASSERT(xfs_buftarg_is_mem(bp->b_target));
217 
218         pos = BBTOB(xfs_buf_daddr(bp));
219         shmem_truncate_range(inode, pos, pos + BBTOB(bp->b_length) - 1);
220 }
221 
222 /*
223  * Finalize a buffer -- discard the backing page if it's stale, or run the
224  * write verifier to detect problems.
225  */
226 int
227 xmbuf_finalize(
228         struct xfs_buf          *bp)
229 {
230         xfs_failaddr_t          fa;
231         int                     error = 0;
232 
233         if (bp->b_flags & XBF_STALE) {
234                 xmbuf_stale(bp);
235                 return 0;
236         }
237 
238         /*
239          * Although this btree is ephemeral, validate the buffer structure so
240          * that we can detect memory corruption errors and software bugs.
241          */
242         fa = bp->b_ops->verify_struct(bp);
243         if (fa) {
244                 error = -EFSCORRUPTED;
245                 xfs_verifier_error(bp, error, fa);
246         }
247 
248         return error;
249 }
250 
251 /*
252  * Detach this xmbuf buffer from the transaction by any means necessary.
253  * All buffers are direct-mapped, so they do not need bwrite.
254  */
255 void
256 xmbuf_trans_bdetach(
257         struct xfs_trans        *tp,
258         struct xfs_buf          *bp)
259 {
260         struct xfs_buf_log_item *bli = bp->b_log_item;
261 
262         ASSERT(bli != NULL);
263 
264         bli->bli_flags &= ~(XFS_BLI_DIRTY | XFS_BLI_ORDERED |
265                             XFS_BLI_LOGGED | XFS_BLI_STALE);
266         clear_bit(XFS_LI_DIRTY, &bli->bli_item.li_flags);
267 
268         while (bp->b_log_item != NULL)
269                 xfs_trans_bdetach(tp, bp);
270 }
271 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php