Path: blob/master/arch/alpha/lib/csum_partial_copy.c
10817 views
/*1* csum_partial_copy - do IP checksumming and copy2*3* (C) Copyright 1996 Linus Torvalds4* accelerated versions (and 21264 assembly versions ) contributed by5* Rick Gorton <[email protected]>6*7* Don't look at this too closely - you'll go mad. The things8* we do for performance..9*/1011#include <linux/types.h>12#include <linux/string.h>13#include <asm/uaccess.h>141516#define ldq_u(x,y) \17__asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y)))1819#define stq_u(x,y) \20__asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))2122#define extql(x,y,z) \23__asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))2425#define extqh(x,y,z) \26__asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))2728#define mskql(x,y,z) \29__asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))3031#define mskqh(x,y,z) \32__asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))3334#define insql(x,y,z) \35__asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))3637#define insqh(x,y,z) \38__asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))394041#define __get_user_u(x,ptr) \42({ \43long __guu_err; \44__asm__ __volatile__( \45"1: ldq_u %0,%2\n" \46"2:\n" \47".section __ex_table,\"a\"\n" \48" .long 1b - .\n" \49" lda %0,2b-1b(%1)\n" \50".previous" \51: "=r"(x), "=r"(__guu_err) \52: "m"(__m(ptr)), "1"(0)); \53__guu_err; \54})5556#define __put_user_u(x,ptr) \57({ \58long __puu_err; \59__asm__ __volatile__( \60"1: stq_u %2,%1\n" \61"2:\n" \62".section __ex_table,\"a\"\n" \63" .long 1b - ." \64" lda $31,2b-1b(%0)\n" \65".previous" \66: "=r"(__puu_err) \67: "m"(__m(addr)), "rJ"(x), "0"(0)); \68__puu_err; \69})707172static inline unsigned short from64to16(unsigned long x)73{74/* Using extract instructions is a bit more efficient75than the original shift/bitmask version. */7677union {78unsigned long ul;79unsigned int ui[2];80unsigned short us[4];81} in_v, tmp_v, out_v;8283in_v.ul = x;84tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];8586/* Since the bits of tmp_v.sh[3] are going to always be zero,87we don't have to bother to add that in. */88out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]89+ (unsigned long) tmp_v.us[2];9091/* Similarly, out_v.us[2] is always zero for the final add. */92return out_v.us[0] + out_v.us[1];93}94959697/*98* Ok. This isn't fun, but this is the EASY case.99*/100static inline unsigned long101csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst,102long len, unsigned long checksum,103int *errp)104{105unsigned long carry = 0;106int err = 0;107108while (len >= 0) {109unsigned long word;110err |= __get_user(word, src);111checksum += carry;112src++;113checksum += word;114len -= 8;115carry = checksum < word;116*dst = word;117dst++;118}119len += 8;120checksum += carry;121if (len) {122unsigned long word, tmp;123err |= __get_user(word, src);124tmp = *dst;125mskql(word, len, word);126checksum += word;127mskqh(tmp, len, tmp);128carry = checksum < word;129*dst = word | tmp;130checksum += carry;131}132if (err) *errp = err;133return checksum;134}135136/*137* This is even less fun, but this is still reasonably138* easy.139*/140static inline unsigned long141csum_partial_cfu_dest_aligned(const unsigned long __user *src,142unsigned long *dst,143unsigned long soff,144long len, unsigned long checksum,145int *errp)146{147unsigned long first;148unsigned long word, carry;149unsigned long lastsrc = 7+len+(unsigned long)src;150int err = 0;151152err |= __get_user_u(first,src);153carry = 0;154while (len >= 0) {155unsigned long second;156157err |= __get_user_u(second, src+1);158extql(first, soff, word);159len -= 8;160src++;161extqh(second, soff, first);162checksum += carry;163word |= first;164first = second;165checksum += word;166*dst = word;167dst++;168carry = checksum < word;169}170len += 8;171checksum += carry;172if (len) {173unsigned long tmp;174unsigned long second;175err |= __get_user_u(second, lastsrc);176tmp = *dst;177extql(first, soff, word);178extqh(second, soff, first);179word |= first;180mskql(word, len, word);181checksum += word;182mskqh(tmp, len, tmp);183carry = checksum < word;184*dst = word | tmp;185checksum += carry;186}187if (err) *errp = err;188return checksum;189}190191/*192* This is slightly less fun than the above..193*/194static inline unsigned long195csum_partial_cfu_src_aligned(const unsigned long __user *src,196unsigned long *dst,197unsigned long doff,198long len, unsigned long checksum,199unsigned long partial_dest,200int *errp)201{202unsigned long carry = 0;203unsigned long word;204unsigned long second_dest;205int err = 0;206207mskql(partial_dest, doff, partial_dest);208while (len >= 0) {209err |= __get_user(word, src);210len -= 8;211insql(word, doff, second_dest);212checksum += carry;213stq_u(partial_dest | second_dest, dst);214src++;215checksum += word;216insqh(word, doff, partial_dest);217carry = checksum < word;218dst++;219}220len += 8;221if (len) {222checksum += carry;223err |= __get_user(word, src);224mskql(word, len, word);225len -= 8;226checksum += word;227insql(word, doff, second_dest);228len += doff;229carry = checksum < word;230partial_dest |= second_dest;231if (len >= 0) {232stq_u(partial_dest, dst);233if (!len) goto out;234dst++;235insqh(word, doff, partial_dest);236}237doff = len;238}239ldq_u(second_dest, dst);240mskqh(second_dest, doff, second_dest);241stq_u(partial_dest | second_dest, dst);242out:243checksum += carry;244if (err) *errp = err;245return checksum;246}247248/*249* This is so totally un-fun that it's frightening. Don't250* look at this too closely, you'll go blind.251*/252static inline unsigned long253csum_partial_cfu_unaligned(const unsigned long __user * src,254unsigned long * dst,255unsigned long soff, unsigned long doff,256long len, unsigned long checksum,257unsigned long partial_dest,258int *errp)259{260unsigned long carry = 0;261unsigned long first;262unsigned long lastsrc;263int err = 0;264265err |= __get_user_u(first, src);266lastsrc = 7+len+(unsigned long)src;267mskql(partial_dest, doff, partial_dest);268while (len >= 0) {269unsigned long second, word;270unsigned long second_dest;271272err |= __get_user_u(second, src+1);273extql(first, soff, word);274checksum += carry;275len -= 8;276extqh(second, soff, first);277src++;278word |= first;279first = second;280insql(word, doff, second_dest);281checksum += word;282stq_u(partial_dest | second_dest, dst);283carry = checksum < word;284insqh(word, doff, partial_dest);285dst++;286}287len += doff;288checksum += carry;289if (len >= 0) {290unsigned long second, word;291unsigned long second_dest;292293err |= __get_user_u(second, lastsrc);294extql(first, soff, word);295extqh(second, soff, first);296word |= first;297first = second;298mskql(word, len-doff, word);299checksum += word;300insql(word, doff, second_dest);301carry = checksum < word;302stq_u(partial_dest | second_dest, dst);303if (len) {304ldq_u(second_dest, dst+1);305insqh(word, doff, partial_dest);306mskqh(second_dest, len, second_dest);307stq_u(partial_dest | second_dest, dst+1);308}309checksum += carry;310} else {311unsigned long second, word;312unsigned long second_dest;313314err |= __get_user_u(second, lastsrc);315extql(first, soff, word);316extqh(second, soff, first);317word |= first;318ldq_u(second_dest, dst);319mskql(word, len-doff, word);320checksum += word;321mskqh(second_dest, len, second_dest);322carry = checksum < word;323insql(word, doff, word);324stq_u(partial_dest | word | second_dest, dst);325checksum += carry;326}327if (err) *errp = err;328return checksum;329}330331__wsum332csum_partial_copy_from_user(const void __user *src, void *dst, int len,333__wsum sum, int *errp)334{335unsigned long checksum = (__force u32) sum;336unsigned long soff = 7 & (unsigned long) src;337unsigned long doff = 7 & (unsigned long) dst;338339if (len) {340if (!doff) {341if (!soff)342checksum = csum_partial_cfu_aligned(343(const unsigned long __user *) src,344(unsigned long *) dst,345len-8, checksum, errp);346else347checksum = csum_partial_cfu_dest_aligned(348(const unsigned long __user *) src,349(unsigned long *) dst,350soff, len-8, checksum, errp);351} else {352unsigned long partial_dest;353ldq_u(partial_dest, dst);354if (!soff)355checksum = csum_partial_cfu_src_aligned(356(const unsigned long __user *) src,357(unsigned long *) dst,358doff, len-8, checksum,359partial_dest, errp);360else361checksum = csum_partial_cfu_unaligned(362(const unsigned long __user *) src,363(unsigned long *) dst,364soff, doff, len-8, checksum,365partial_dest, errp);366}367checksum = from64to16 (checksum);368}369return (__force __wsum)checksum;370}371372__wsum373csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)374{375return csum_partial_copy_from_user((__force const void __user *)src,376dst, len, sum, NULL);377}378379380