/* * compatibility for older kernels. this code is mostly taken from include/linux/uio.h, * include/linuxfs/fs.h and associated .c files. * * the originals are licensed as: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #pragma once #include #include #include #include #include #ifndef KERNEL_HAS_ITER_KVEC #error ITER_KVEC is a required feature #endif #ifndef KERNEL_HAS_ITER_IS_IOVEC #error iter_is_iovec() is a required feature #endif /* * In kernels 3.15 to 6.3 there was iov_iter_iovec(), returning the first iovec * in an iov_iter of type ITER_IOVEC. * 6.4 removes and started using macro iter_iov_addr & iter_iov_len. * Using those now and providing a shim for older kernels. */ #if !defined(KERNEL_HAS_ITER_IOV_ADDR) #define iter_iov_addr(iter) (iter_iov(iter)->iov_base + (iter)->iov_offset) #define iter_iov_len(iter) (iter_iov(iter)->iov_len - (iter)->iov_offset) #endif #ifndef KERNEL_HAS_IOV_ITER_INIT_DIR #error We require kernels that have a "direction" parameter to iov_iter_init(). #endif #ifndef KERNEL_HAS_IOV_ITER_TYPE static inline int iov_iter_type(const struct iov_iter *i) { return i->type & ~(READ | WRITE); } #endif #ifndef KERNEL_HAS_IOV_ITER_IS_PIPE static inline bool iov_iter_is_pipe(struct iov_iter* iter) { #ifdef KERNEL_HAS_ITER_PIPE return iov_iter_type(iter) == ITER_PIPE; #else return false; #endif } #endif static inline int beegfs_iov_iter_is_iovec(const struct iov_iter *iter) { return iov_iter_type(iter) == ITER_IOVEC; } // TODO: Now that ITER_KVEC is required across all kernels, is this function still needed? static inline struct iov_iter *beegfs_get_iovec_iov_iter(struct iov_iter *iter) { BUG_ON(!beegfs_iov_iter_is_iovec(iter)); return iter; } static inline unsigned long beegfs_iov_iter_nr_segs(const struct iov_iter *iter) { return iter->nr_segs; } static inline void beegfs_iov_iter_clear(struct iov_iter *iter) { iter->count = 0; } #ifdef KERNEL_HAS_ITER_PIPE static inline bool beegfs_is_pipe_iter(struct iov_iter * iter) { return iov_iter_type(iter) == ITER_PIPE; } #endif #define BEEGFS_IOV_ITER_INIT iov_iter_init static inline void BEEGFS_IOV_ITER_KVEC(struct iov_iter *iter, int direction, const struct kvec* kvec, unsigned long nr_segs, size_t count) { #ifndef KERNEL_HAS_IOV_ITER_KVEC_NO_TYPE_FLAG_IN_DIRECTION direction |= ITER_KVEC; #endif iov_iter_kvec(iter, direction, kvec, nr_segs, count); } static inline void BEEGFS_IOV_ITER_BVEC(struct iov_iter *iter, int direction, const struct bio_vec* bvec, unsigned long nr_segs, size_t count) { #ifndef KERNEL_HAS_IOV_ITER_KVEC_NO_TYPE_FLAG_IN_DIRECTION direction |= ITER_BVEC; #endif iov_iter_bvec(iter, direction, bvec, nr_segs, count); } /* BeeGFS_ReadSink We can't get parallel reads to work easily with ITER_PIPE. That type of iter doesn't allow splitting up a region easily for parallel writing. The reason is that the iov_iter_advance() implementation for ITER_PIPE modifies shared state (the pipe_inode structure). The BeeGFS_ReadSink structure allows to abstract from that concern by converting to an ITER_BVEC iter where necessary. Use is as follows: 1) Initialize the struct by zeroing out, or using a {0} initializer. This allows the cleanup routine to work even if nothing was ever allocated. 1) Call _reserve() to set up a view of the given size into a given iov_iter struct. If the given iov_iter is not of type ITER_PIPE, it will be copied straight to the "sanitized_iter" field. Otherwise (if it is an ITER_PIPE), an ITER_BVEC iterator will be made by allocating pages from the pipe and setting up a bio_vec for each page. Note that this can fail in low memory situations. The size of the view that was successfully allocated can be queried by calling iov_iter_count() on the sanitized_iter field. 2) The sanitized_iter field should be used to read data. The field can be used destructively. In particular it is safe to call iov_iter_advance() on it in order to partition the view for multiple parallel reads. 3) When reads are done, probably, iov_iter_advance() should be called on the iter that was given to _reserve(). 4) Call _release() to give back the pages that were reserved in step 2). If the struct was properly initialized in step 1), is safe to call _release() even if _reserve() was never called. This is useful when cleaning up state after an early exit. 5) Go back to 2) if necessary, to copy more data. */ typedef struct _BeeGFS_ReadSink BeeGFS_ReadSink; struct _BeeGFS_ReadSink { size_t npages; // Number of pages currently in use (get_page()) struct page **pages; // 0..npages struct bio_vec *bvecs; // 0..npages // output value struct iov_iter sanitized_iter; }; void beegfs_readsink_reserve(BeeGFS_ReadSink *rs, struct iov_iter *iter, size_t size); void beegfs_readsink_release(BeeGFS_ReadSink *rs); /* We have lots of code locations where we need to read or write memory using a pointer + length pair, but need to use an iov_iter based API. This always leads to boilerplate where struct iovec and struct iov_iter values have to be declared on the stack. The following hack is meant to reduce that boilerplate. */ #define STACK_ALLOC_BEEGFS_ITER_IOV(ptr, size, direction) \ ___BEEGFS_IOV_ITER_INIT(&(struct iov_iter){0}, &(struct iovec){0}, (ptr), (size), (direction)) #define STACK_ALLOC_BEEGFS_ITER_KVEC(ptr, size, direction) \ ___BEEGFS_IOV_ITER_KVEC(&(struct iov_iter){0}, &(struct kvec){0}, (ptr), (size), (direction)) static inline struct iov_iter *___BEEGFS_IOV_ITER_INIT( struct iov_iter *iter, struct iovec *iovec, const char __user *ptr, size_t size, int direction) { unsigned nr_segs = 1; *iovec = (struct iovec) { .iov_base = (char __user *) ptr, .iov_len = size, }; BEEGFS_IOV_ITER_INIT(iter, direction, iovec, nr_segs, size); return iter; } static inline struct iov_iter *___BEEGFS_IOV_ITER_KVEC( struct iov_iter *iter, struct kvec* kvec, const char *ptr, size_t size, int direction) { unsigned nr_segs = 1; *kvec = (struct kvec) { .iov_base = (char *) ptr, .iov_len = size, }; BEEGFS_IOV_ITER_KVEC(iter, direction, kvec, nr_segs, size); return iter; }