Re: [PATCH 00/29] RFC: iov_iter: Switch to using an ops table
From: Matthew Wilcox <willy@infradead.org>
Date: 2020-12-11 03:25:45
Also in:
linux-fsdevel, lkml
On Sat, Nov 21, 2020 at 02:13:21PM +0000, David Howells wrote:
I had a go switching the iov_iter stuff away from using a type bitmask to using an ops table to get rid of the if-if-if-if chains that are all over the place. After I pushed it, someone pointed me at Pavel's two patches. I have another iterator class that I want to add - which would lengthen the if-if-if-if chains. A lot of the time, there's a conditional clause at the beginning of a function that just jumps off to a type-specific handler or to reject the operation for that type. An ops table can just point to that instead.
So, given the performance problem, how about turning this inside out?
struct iov_step {
union {
void *kaddr;
void __user *uaddr;
};
unsigned int len;
bool user_addr;
bool kmap;
struct page *page;
};
bool iov_iterate(struct iov_step *step, struct iov_iter *i, size_t max)
{
if (step->page)
kunmap(page)
else if (step->kmap)
kunmap_atomic(step->kaddr);
if (max == 0)
return false;
if (i->type & ITER_IOVEC) {
step->user_addr = true;
step->uaddr = i->iov.iov_base + i->iov_offset;
return true;
}
if (i->type & ITER_BVEC) {
... get the page ...
} else if (i->type & ITER_KVEC) {
... get the page ...
} else ...
kmap or kmap_atomic as appropriate ...
...set kaddr & len ...
return true;
}
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
{
struct iov_step step = {};
while (iov_iterate(&step, i, bytes)) {
if (user_addr)
copy_from_user(addr, step.uaddr, step.len);
else
memcpy(addr, step.kaddr, step.len);
bytes -= step.len;
}
}