2025-08-10 01:34:16 +02:00

211 lines
6.5 KiB
C

/*
* 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 <linux/kernel.h>
#include <linux/uio.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include <linux/bvec.h>
#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;
}