OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [core/] [iovec.c] - Diff between revs 1278 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1278 Rev 1765
/*
/*
 *      iovec manipulation routines.
 *      iovec manipulation routines.
 *
 *
 *
 *
 *              This program is free software; you can redistribute it and/or
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *              2 of the License, or (at your option) any later version.
 *
 *
 *      Fixes:
 *      Fixes:
 *              Andrew Lunn     :       Errors in iovec copying.
 *              Andrew Lunn     :       Errors in iovec copying.
 *              Pedro Roque     :       Added memcpy_fromiovecend and
 *              Pedro Roque     :       Added memcpy_fromiovecend and
 *                                      csum_..._fromiovecend.
 *                                      csum_..._fromiovecend.
 *              Andi Kleen      :       fixed error handling for 2.1
 *              Andi Kleen      :       fixed error handling for 2.1
 *              Alexey Kuznetsov:       2.1 optimisations
 *              Alexey Kuznetsov:       2.1 optimisations
 *              Andi Kleen      :       Fix csum*fromiovecend for IPv6.
 *              Andi Kleen      :       Fix csum*fromiovecend for IPv6.
 */
 */
 
 
 
 
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/net.h>
#include <linux/net.h>
#include <linux/in6.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <asm/byteorder.h>
#include <net/checksum.h>
#include <net/checksum.h>
#include <net/sock.h>
#include <net/sock.h>
 
 
/*
/*
 *      Verify iovec. The caller must ensure that the iovec is big enough
 *      Verify iovec. The caller must ensure that the iovec is big enough
 *      to hold the message iovec.
 *      to hold the message iovec.
 *
 *
 *      Save time not doing verify_area. copy_*_user will make this work
 *      Save time not doing verify_area. copy_*_user will make this work
 *      in any case.
 *      in any case.
 */
 */
 
 
int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
{
{
        int size, err, ct;
        int size, err, ct;
 
 
        if(m->msg_namelen)
        if(m->msg_namelen)
        {
        {
                if(mode==VERIFY_READ)
                if(mode==VERIFY_READ)
                {
                {
                        err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
                        err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
                        if(err<0)
                        if(err<0)
                                goto out;
                                goto out;
                }
                }
 
 
                m->msg_name = address;
                m->msg_name = address;
        } else
        } else
                m->msg_name = NULL;
                m->msg_name = NULL;
 
 
        err = -EFAULT;
        err = -EFAULT;
        size = m->msg_iovlen * sizeof(struct iovec);
        size = m->msg_iovlen * sizeof(struct iovec);
        if (copy_from_user(iov, m->msg_iov, size))
        if (copy_from_user(iov, m->msg_iov, size))
                goto out;
                goto out;
        m->msg_iov=iov;
        m->msg_iov=iov;
 
 
        for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) {
        for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) {
                err += iov[ct].iov_len;
                err += iov[ct].iov_len;
                /* Goal is not to verify user data, but to prevent returning
                /* Goal is not to verify user data, but to prevent returning
                   negative value, which is interpreted as errno.
                   negative value, which is interpreted as errno.
                   Overflow is still possible, but it is harmless.
                   Overflow is still possible, but it is harmless.
                 */
                 */
                if (err < 0)
                if (err < 0)
                        return -EMSGSIZE;
                        return -EMSGSIZE;
        }
        }
out:
out:
        return err;
        return err;
}
}
 
 
/*
/*
 *      Copy kernel to iovec. Returns -EFAULT on error.
 *      Copy kernel to iovec. Returns -EFAULT on error.
 *
 *
 *      Note: this modifies the original iovec.
 *      Note: this modifies the original iovec.
 */
 */
 
 
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
{
{
        int err = -EFAULT;
        int err = -EFAULT;
 
 
        while(len>0)
        while(len>0)
        {
        {
                if(iov->iov_len)
                if(iov->iov_len)
                {
                {
                        int copy = min_t(unsigned int, iov->iov_len, len);
                        int copy = min_t(unsigned int, iov->iov_len, len);
                        if (copy_to_user(iov->iov_base, kdata, copy))
                        if (copy_to_user(iov->iov_base, kdata, copy))
                                goto out;
                                goto out;
                        kdata+=copy;
                        kdata+=copy;
                        len-=copy;
                        len-=copy;
                        iov->iov_len-=copy;
                        iov->iov_len-=copy;
                        iov->iov_base+=copy;
                        iov->iov_base+=copy;
                }
                }
                iov++;
                iov++;
        }
        }
        err = 0;
        err = 0;
out:
out:
        return err;
        return err;
}
}
 
 
/*
/*
 *      In kernel copy to iovec. Returns -EFAULT on error.
 *      In kernel copy to iovec. Returns -EFAULT on error.
 *
 *
 *      Note: this modifies the original iovec.
 *      Note: this modifies the original iovec.
 */
 */
 
 
void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
{
{
        while(len>0)
        while(len>0)
        {
        {
                if(iov->iov_len)
                if(iov->iov_len)
                {
                {
                        int copy = min_t(unsigned int, iov->iov_len, len);
                        int copy = min_t(unsigned int, iov->iov_len, len);
                        memcpy(iov->iov_base, kdata, copy);
                        memcpy(iov->iov_base, kdata, copy);
                        kdata+=copy;
                        kdata+=copy;
                        len-=copy;
                        len-=copy;
                        iov->iov_len-=copy;
                        iov->iov_len-=copy;
                        iov->iov_base+=copy;
                        iov->iov_base+=copy;
                }
                }
                iov++;
                iov++;
        }
        }
}
}
 
 
 
 
/*
/*
 *      Copy iovec to kernel. Returns -EFAULT on error.
 *      Copy iovec to kernel. Returns -EFAULT on error.
 *
 *
 *      Note: this modifies the original iovec.
 *      Note: this modifies the original iovec.
 */
 */
 
 
int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
{
{
        int err = -EFAULT;
        int err = -EFAULT;
 
 
        while(len>0)
        while(len>0)
        {
        {
                if(iov->iov_len)
                if(iov->iov_len)
                {
                {
                        int copy = min_t(unsigned int, len, iov->iov_len);
                        int copy = min_t(unsigned int, len, iov->iov_len);
                        if (copy_from_user(kdata, iov->iov_base, copy))
                        if (copy_from_user(kdata, iov->iov_base, copy))
                                goto out;
                                goto out;
                        len-=copy;
                        len-=copy;
                        kdata+=copy;
                        kdata+=copy;
                        iov->iov_base+=copy;
                        iov->iov_base+=copy;
                        iov->iov_len-=copy;
                        iov->iov_len-=copy;
                }
                }
                iov++;
                iov++;
        }
        }
        err = 0;
        err = 0;
out:
out:
        return err;
        return err;
}
}
 
 
 
 
/*
/*
 *      For use with ip_build_xmit
 *      For use with ip_build_xmit
 */
 */
 
 
int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
                        int len)
                        int len)
{
{
        int err = -EFAULT;
        int err = -EFAULT;
 
 
        /* Skip over the finished iovecs */
        /* Skip over the finished iovecs */
        while(offset >= iov->iov_len)
        while(offset >= iov->iov_len)
        {
        {
                offset -= iov->iov_len;
                offset -= iov->iov_len;
                iov++;
                iov++;
        }
        }
 
 
        while (len > 0)
        while (len > 0)
        {
        {
                u8 *base = iov->iov_base + offset;
                u8 *base = iov->iov_base + offset;
                int copy = min_t(unsigned int, len, iov->iov_len - offset);
                int copy = min_t(unsigned int, len, iov->iov_len - offset);
 
 
                offset = 0;
                offset = 0;
                if (copy_from_user(kdata, base, copy))
                if (copy_from_user(kdata, base, copy))
                        goto out;
                        goto out;
                len   -= copy;
                len   -= copy;
                kdata += copy;
                kdata += copy;
                iov++;
                iov++;
        }
        }
        err = 0;
        err = 0;
out:
out:
        return err;
        return err;
}
}
 
 
/*
/*
 *      And now for the all-in-one: copy and checksum from a user iovec
 *      And now for the all-in-one: copy and checksum from a user iovec
 *      directly to a datagram
 *      directly to a datagram
 *      Calls to csum_partial but the last must be in 32 bit chunks
 *      Calls to csum_partial but the last must be in 32 bit chunks
 *
 *
 *      ip_build_xmit must ensure that when fragmenting only the last
 *      ip_build_xmit must ensure that when fragmenting only the last
 *      call to this function will be unaligned also.
 *      call to this function will be unaligned also.
 */
 */
 
 
int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
                                 int offset, unsigned int len, int *csump)
                                 int offset, unsigned int len, int *csump)
{
{
        int csum = *csump;
        int csum = *csump;
        int partial_cnt = 0, err = 0;
        int partial_cnt = 0, err = 0;
 
 
        /* Skip over the finished iovecs */
        /* Skip over the finished iovecs */
        while (offset >= iov->iov_len)
        while (offset >= iov->iov_len)
        {
        {
                offset -= iov->iov_len;
                offset -= iov->iov_len;
                iov++;
                iov++;
        }
        }
 
 
        while (len > 0)
        while (len > 0)
        {
        {
                u8 *base = iov->iov_base + offset;
                u8 *base = iov->iov_base + offset;
                int copy = min_t(unsigned int, len, iov->iov_len - offset);
                int copy = min_t(unsigned int, len, iov->iov_len - offset);
 
 
                offset = 0;
                offset = 0;
                /* There is a remnant from previous iov. */
                /* There is a remnant from previous iov. */
                if (partial_cnt)
                if (partial_cnt)
                {
                {
                        int par_len = 4 - partial_cnt;
                        int par_len = 4 - partial_cnt;
 
 
                        /* iov component is too short ... */
                        /* iov component is too short ... */
                        if (par_len > copy) {
                        if (par_len > copy) {
                                if (copy_from_user(kdata, base, copy))
                                if (copy_from_user(kdata, base, copy))
                                        goto out_fault;
                                        goto out_fault;
                                kdata += copy;
                                kdata += copy;
                                base  += copy;
                                base  += copy;
                                partial_cnt += copy;
                                partial_cnt += copy;
                                len   -= copy;
                                len   -= copy;
                                iov++;
                                iov++;
                                if (len)
                                if (len)
                                        continue;
                                        continue;
                                *csump = csum_partial(kdata - partial_cnt,
                                *csump = csum_partial(kdata - partial_cnt,
                                                         partial_cnt, csum);
                                                         partial_cnt, csum);
                                goto out;
                                goto out;
                        }
                        }
                        if (copy_from_user(kdata, base, par_len))
                        if (copy_from_user(kdata, base, par_len))
                                goto out_fault;
                                goto out_fault;
                        csum = csum_partial(kdata - partial_cnt, 4, csum);
                        csum = csum_partial(kdata - partial_cnt, 4, csum);
                        kdata += par_len;
                        kdata += par_len;
                        base  += par_len;
                        base  += par_len;
                        copy  -= par_len;
                        copy  -= par_len;
                        len   -= par_len;
                        len   -= par_len;
                        partial_cnt = 0;
                        partial_cnt = 0;
                }
                }
 
 
                if (len > copy)
                if (len > copy)
                {
                {
                        partial_cnt = copy % 4;
                        partial_cnt = copy % 4;
                        if (partial_cnt)
                        if (partial_cnt)
                        {
                        {
                                copy -= partial_cnt;
                                copy -= partial_cnt;
                                if (copy_from_user(kdata + copy, base + copy,
                                if (copy_from_user(kdata + copy, base + copy,
                                                partial_cnt))
                                                partial_cnt))
                                        goto out_fault;
                                        goto out_fault;
                        }
                        }
                }
                }
 
 
                if (copy) {
                if (copy) {
                        csum = csum_and_copy_from_user(base, kdata, copy,
                        csum = csum_and_copy_from_user(base, kdata, copy,
                                                        csum, &err);
                                                        csum, &err);
                        if (err)
                        if (err)
                                goto out;
                                goto out;
                }
                }
                len   -= copy + partial_cnt;
                len   -= copy + partial_cnt;
                kdata += copy + partial_cnt;
                kdata += copy + partial_cnt;
                iov++;
                iov++;
        }
        }
        *csump = csum;
        *csump = csum;
out:
out:
        return err;
        return err;
 
 
out_fault:
out_fault:
        err = -EFAULT;
        err = -EFAULT;
        goto out;
        goto out;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.