csum-partial_64.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * arch/x86_64/lib/csum-partial.c
  4. *
  5. * This file contains network checksum routines that are better done
  6. * in an architecture-specific manner due to speed.
  7. */
  8. #include <linux/compiler.h>
  9. #include <linux/export.h>
  10. #include <asm/checksum.h>
  11. #include <asm/word-at-a-time.h>
  12. static inline unsigned short from32to16(unsigned a)
  13. {
  14. unsigned short b = a >> 16;
  15. asm("addw %w2,%w0\n\t"
  16. "adcw $0,%w0\n"
  17. : "=r" (b)
  18. : "0" (b), "r" (a));
  19. return b;
  20. }
  21. /*
  22. * Do a checksum on an arbitrary memory area.
  23. * Returns a 32bit checksum.
  24. *
  25. * This isn't as time critical as it used to be because many NICs
  26. * do hardware checksumming these days.
  27. *
  28. * Still, with CHECKSUM_COMPLETE this is called to compute
  29. * checksums on IPv6 headers (40 bytes) and other small parts.
  30. * it's best to have buff aligned on a 64-bit boundary
  31. */
  32. __wsum csum_partial(const void *buff, int len, __wsum sum)
  33. {
  34. u64 temp64 = (__force u64)sum;
  35. unsigned odd, result;
  36. odd = 1 & (unsigned long) buff;
  37. if (unlikely(odd)) {
  38. if (unlikely(len == 0))
  39. return sum;
  40. temp64 = ror32((__force u32)sum, 8);
  41. temp64 += (*(unsigned char *)buff << 8);
  42. len--;
  43. buff++;
  44. }
  45. while (unlikely(len >= 64)) {
  46. asm("addq 0*8(%[src]),%[res]\n\t"
  47. "adcq 1*8(%[src]),%[res]\n\t"
  48. "adcq 2*8(%[src]),%[res]\n\t"
  49. "adcq 3*8(%[src]),%[res]\n\t"
  50. "adcq 4*8(%[src]),%[res]\n\t"
  51. "adcq 5*8(%[src]),%[res]\n\t"
  52. "adcq 6*8(%[src]),%[res]\n\t"
  53. "adcq 7*8(%[src]),%[res]\n\t"
  54. "adcq $0,%[res]"
  55. : [res] "+r" (temp64)
  56. : [src] "r" (buff)
  57. : "memory");
  58. buff += 64;
  59. len -= 64;
  60. }
  61. if (len & 32) {
  62. asm("addq 0*8(%[src]),%[res]\n\t"
  63. "adcq 1*8(%[src]),%[res]\n\t"
  64. "adcq 2*8(%[src]),%[res]\n\t"
  65. "adcq 3*8(%[src]),%[res]\n\t"
  66. "adcq $0,%[res]"
  67. : [res] "+r" (temp64)
  68. : [src] "r" (buff)
  69. : "memory");
  70. buff += 32;
  71. }
  72. if (len & 16) {
  73. asm("addq 0*8(%[src]),%[res]\n\t"
  74. "adcq 1*8(%[src]),%[res]\n\t"
  75. "adcq $0,%[res]"
  76. : [res] "+r" (temp64)
  77. : [src] "r" (buff)
  78. : "memory");
  79. buff += 16;
  80. }
  81. if (len & 8) {
  82. asm("addq 0*8(%[src]),%[res]\n\t"
  83. "adcq $0,%[res]"
  84. : [res] "+r" (temp64)
  85. : [src] "r" (buff)
  86. : "memory");
  87. buff += 8;
  88. }
  89. if (len & 7) {
  90. unsigned int shift = (8 - (len & 7)) * 8;
  91. unsigned long trail;
  92. trail = (load_unaligned_zeropad(buff) << shift) >> shift;
  93. asm("addq %[trail],%[res]\n\t"
  94. "adcq $0,%[res]"
  95. : [res] "+r" (temp64)
  96. : [trail] "r" (trail));
  97. }
  98. result = add32_with_carry(temp64 >> 32, temp64 & 0xffffffff);
  99. if (unlikely(odd)) {
  100. result = from32to16(result);
  101. result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  102. }
  103. return (__force __wsum)result;
  104. }
  105. EXPORT_SYMBOL(csum_partial);
  106. /*
  107. * this routine is used for miscellaneous IP-like checksums, mainly
  108. * in icmp.c
  109. */
  110. __sum16 ip_compute_csum(const void *buff, int len)
  111. {
  112. return csum_fold(csum_partial(buff,len,0));
  113. }
  114. EXPORT_SYMBOL(ip_compute_csum);