| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #ifndef __LIBXFS_IO_H_ |
| #define __LIBXFS_IO_H_ |
| |
| /* |
| * Kernel equivalent buffer based I/O interface |
| */ |
| |
| struct xfs_buf; |
| struct xfs_mount; |
| struct xfs_perag; |
| struct libxfs_init; |
| |
| /* |
| * IO verifier callbacks need the xfs_mount pointer, so we have to behave |
| * somewhat like the kernel now for userspace IO in terms of having buftarg |
| * based devices... |
| */ |
| struct xfs_buftarg { |
| struct xfs_mount *bt_mount; |
| pthread_mutex_t lock; |
| unsigned long writes_left; |
| dev_t bt_bdev; |
| int bt_bdev_fd; |
| struct xfile *bt_xfile; |
| unsigned int flags; |
| struct cache *bcache; /* buffer cache */ |
| }; |
| |
| /* We purged a dirty buffer and lost a write. */ |
| #define XFS_BUFTARG_LOST_WRITE (1 << 0) |
| /* A dirty buffer failed the write verifier. */ |
| #define XFS_BUFTARG_CORRUPT_WRITE (1 << 1) |
| /* Simulate failure after a certain number of writes. */ |
| #define XFS_BUFTARG_INJECT_WRITE_FAIL (1 << 2) |
| /* purge buffers when lookups find a size mismatch */ |
| #define XFS_BUFTARG_MISCOMPARE_PURGE (1 << 3) |
| |
| /* Simulate the system crashing after a certain number of writes. */ |
| static inline void |
| xfs_buftarg_trip_write( |
| struct xfs_buftarg *btp) |
| { |
| if (!(btp->flags & XFS_BUFTARG_INJECT_WRITE_FAIL)) |
| return; |
| |
| pthread_mutex_lock(&btp->lock); |
| btp->writes_left--; |
| if (!btp->writes_left) |
| platform_crash(); |
| pthread_mutex_unlock(&btp->lock); |
| } |
| |
| void libxfs_buftarg_init(struct xfs_mount *mp, struct libxfs_init *xi); |
| int libxfs_blkdev_issue_flush(struct xfs_buftarg *btp); |
| |
| /* |
| * The bufkey is used to pass the new buffer information to the cache object |
| * allocation routine. Because discontiguous buffers need to pass different |
| * information, we need fields to pass that information. However, because the |
| * blkno and bblen is needed for the initial cache entry lookup (i.e. for |
| * bcompare) the fact that the map/nmaps is non-null to switch to discontiguous |
| * buffer initialisation instead of a contiguous buffer. |
| */ |
| struct xfs_bufkey { |
| struct xfs_buftarg *buftarg; |
| xfs_daddr_t blkno; |
| unsigned int bblen; |
| struct xfs_buf_map *map; |
| int nmaps; |
| }; |
| |
| /* for buf_mem.c only: */ |
| unsigned int libxfs_bhash(cache_key_t key, unsigned int hashsize, |
| unsigned int hashshift); |
| int libxfs_bcompare(struct cache_node *node, cache_key_t key); |
| |
| #define LIBXFS_BBTOOFF64(bbs) (((xfs_off_t)(bbs)) << BBSHIFT) |
| |
| #define XB_PAGES 2 |
| struct xfs_buf_map { |
| xfs_daddr_t bm_bn; /* block number for I/O */ |
| int bm_len; /* size of I/O */ |
| }; |
| |
| #define DEFINE_SINGLE_BUF_MAP(map, blkno, numblk) \ |
| struct xfs_buf_map (map) = { .bm_bn = (blkno), .bm_len = (numblk) }; |
| |
| struct xfs_buf_ops { |
| char *name; |
| union { |
| __be32 magic[2]; /* v4 and v5 on disk magic values */ |
| __be16 magic16[2]; /* v4 and v5 on disk magic values */ |
| }; |
| void (*verify_read)(struct xfs_buf *); |
| void (*verify_write)(struct xfs_buf *); |
| xfs_failaddr_t (*verify_struct)(struct xfs_buf *); |
| }; |
| |
| struct xfs_buf { |
| struct cache_node b_node; |
| unsigned int b_flags; |
| xfs_daddr_t b_cache_key; /* buffer cache index */ |
| unsigned int b_length; |
| struct xfs_buftarg *b_target; |
| pthread_mutex_t b_lock; |
| pthread_t b_holder; |
| unsigned int b_recur; |
| void *b_log_item; |
| struct list_head b_li_list; /* Log items list head */ |
| void *b_transp; |
| void *b_addr; |
| int b_error; |
| const struct xfs_buf_ops *b_ops; |
| struct xfs_perag *b_pag; |
| struct xfs_mount *b_mount; |
| struct xfs_buf_map *b_maps; |
| struct xfs_buf_map __b_map; |
| int b_nmaps; |
| struct list_head b_list; |
| }; |
| |
| bool xfs_verify_magic(struct xfs_buf *bp, __be32 dmagic); |
| bool xfs_verify_magic16(struct xfs_buf *bp, __be16 dmagic); |
| |
| /* b_flags bits */ |
| #define LIBXFS_B_DIRTY 0x0002 /* buffer has been modified */ |
| #define LIBXFS_B_STALE 0x0004 /* buffer marked as invalid */ |
| #define LIBXFS_B_UPTODATE 0x0008 /* buffer is sync'd to disk */ |
| #define LIBXFS_B_DISCONTIG 0x0010 /* discontiguous buffer */ |
| #define LIBXFS_B_UNCHECKED 0x0020 /* needs verification */ |
| |
| typedef unsigned int xfs_buf_flags_t; |
| |
| #define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL)) |
| |
| #define xfs_buf_offset(bp, offset) ((bp)->b_addr + (offset)) |
| |
| static inline xfs_daddr_t xfs_buf_daddr(struct xfs_buf *bp) |
| { |
| return bp->b_maps[0].bm_bn; |
| } |
| |
| static inline void xfs_buf_set_daddr(struct xfs_buf *bp, xfs_daddr_t blkno) |
| { |
| assert(bp->b_cache_key == XFS_BUF_DADDR_NULL); |
| bp->b_maps[0].bm_bn = blkno; |
| } |
| |
| void libxfs_buf_set_priority(struct xfs_buf *bp, int priority); |
| int libxfs_buf_priority(struct xfs_buf *bp); |
| |
| #define xfs_buf_set_ref(bp,ref) ((void) 0) |
| #define xfs_buf_ioerror(bp,err) ((bp)->b_error = (err)) |
| |
| #define xfs_daddr_to_agno(mp,d) \ |
| ((xfs_agnumber_t)(XFS_BB_TO_FSBT(mp, d) / (mp)->m_sb.sb_agblocks)) |
| #define xfs_daddr_to_agbno(mp,d) \ |
| ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp, d) % (mp)->m_sb.sb_agblocks)) |
| |
| /* Buffer Cache Interfaces */ |
| |
| extern struct cache_operations libxfs_bcache_operations; |
| |
| #define LIBXFS_GETBUF_TRYLOCK (1 << 0) |
| |
| /* Return the buffer even if the verifiers fail. */ |
| #define LIBXFS_READBUF_SALVAGE (1 << 1) |
| |
| int libxfs_buf_read_map(struct xfs_buftarg *btp, struct xfs_buf_map *maps, |
| int nmaps, int flags, struct xfs_buf **bpp, |
| const struct xfs_buf_ops *ops); |
| void libxfs_buf_mark_dirty(struct xfs_buf *bp); |
| int libxfs_buf_get_map(struct xfs_buftarg *btp, struct xfs_buf_map *maps, |
| int nmaps, int flags, struct xfs_buf **bpp); |
| void libxfs_buf_relse(struct xfs_buf *bp); |
| |
| static inline int |
| libxfs_buf_get( |
| struct xfs_buftarg *target, |
| xfs_daddr_t blkno, |
| size_t numblks, |
| struct xfs_buf **bpp) |
| { |
| DEFINE_SINGLE_BUF_MAP(map, blkno, numblks); |
| |
| return libxfs_buf_get_map(target, &map, 1, 0, bpp); |
| } |
| |
| static inline int |
| libxfs_buf_read( |
| struct xfs_buftarg *target, |
| xfs_daddr_t blkno, |
| size_t numblks, |
| xfs_buf_flags_t flags, |
| struct xfs_buf **bpp, |
| const struct xfs_buf_ops *ops) |
| { |
| DEFINE_SINGLE_BUF_MAP(map, blkno, numblks); |
| |
| return libxfs_buf_read_map(target, &map, 1, flags, bpp, ops); |
| } |
| |
| int libxfs_readbuf_verify(struct xfs_buf *bp, const struct xfs_buf_ops *ops); |
| struct xfs_buf *libxfs_getsb(struct xfs_mount *mp); |
| struct xfs_buf *libxfs_getrtsb(struct xfs_mount *mp); |
| extern void libxfs_bcache_purge(struct xfs_mount *mp); |
| extern void libxfs_bcache_free(void); |
| extern void libxfs_bcache_flush(struct xfs_mount *mp); |
| extern int libxfs_bcache_overflowed(struct xfs_mount *mp); |
| |
| /* Buffer (Raw) Interfaces */ |
| int libxfs_bwrite(struct xfs_buf *bp); |
| extern int libxfs_readbufr(struct xfs_buftarg *, xfs_daddr_t, struct xfs_buf *, int, int); |
| extern int libxfs_readbufr_map(struct xfs_buftarg *, struct xfs_buf *, int); |
| |
| extern int libxfs_device_zero(struct xfs_buftarg *, xfs_daddr_t, uint); |
| |
| extern int libxfs_bhash_size; |
| |
| static inline int |
| xfs_buf_verify_cksum(struct xfs_buf *bp, unsigned long cksum_offset) |
| { |
| return xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length), |
| cksum_offset); |
| } |
| |
| static inline void |
| xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset) |
| { |
| xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), |
| cksum_offset); |
| } |
| |
| static inline int |
| xfs_buf_associate_memory(struct xfs_buf *bp, void *mem, size_t len) |
| { |
| bp->b_addr = mem; |
| bp->b_length = BTOBB(len); |
| return 0; |
| } |
| |
| static inline void |
| xfs_buf_hold(struct xfs_buf *bp) |
| { |
| bp->b_node.cn_count++; |
| } |
| |
| void xfs_buf_lock(struct xfs_buf *bp); |
| void xfs_buf_unlock(struct xfs_buf *bp); |
| |
| int libxfs_buf_get_uncached(struct xfs_buftarg *targ, size_t bblen, |
| struct xfs_buf **bpp); |
| int libxfs_buf_read_uncached(struct xfs_buftarg *targ, xfs_daddr_t daddr, |
| size_t bblen, int flags, struct xfs_buf **bpp, |
| const struct xfs_buf_ops *ops); |
| |
| /* Push a single buffer on a delwri queue. */ |
| static inline bool |
| xfs_buf_delwri_queue(struct xfs_buf *bp, struct list_head *buffer_list) |
| { |
| xfs_buf_hold(bp); |
| list_add_tail(&bp->b_list, buffer_list); |
| return true; |
| } |
| |
| static inline void |
| xfs_buf_delwri_queue_here(struct xfs_buf *bp, struct list_head *buffer_list) |
| { |
| ASSERT(list_empty(&bp->b_list)); |
| |
| /* This buffer is uptodate; don't let it get reread. */ |
| libxfs_buf_mark_dirty(bp); |
| |
| xfs_buf_delwri_queue(bp, buffer_list); |
| } |
| |
| int xfs_buf_delwri_submit(struct list_head *buffer_list); |
| void xfs_buf_delwri_cancel(struct list_head *list); |
| |
| xfs_daddr_t xfs_buftarg_nr_sectors(struct xfs_buftarg *btp); |
| |
| static inline bool |
| xfs_buftarg_verify_daddr( |
| struct xfs_buftarg *btp, |
| xfs_daddr_t daddr) |
| { |
| return daddr < xfs_buftarg_nr_sectors(btp); |
| } |
| |
| #endif /* __LIBXFS_IO_H__ */ |