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;
        }
}

Reply via email to