diff mbox series

[PATCHv4,6/8] crypto: add rocksoft 64b crc guard tag framework

Message ID 20220303201312.3255347-7-kbusch@kernel.org (mailing list archive)
State New, archived
Headers show
Series 64-bit data integrity field support | expand

Commit Message

Keith Busch March 3, 2022, 8:13 p.m. UTC
Hardware specific features may be able to calculate a crc64, so provide
a framework for drivers to register their implementation. If nothing is
registered, fallback to the generic table lookup implementation. The
implementation is modeled after the crct10dif equivalent.

Signed-off-by: Keith Busch <kbusch@kernel.org>
---
v3->v4:

  Updated naming, and Kconfig help texts, and Kconfig dependency

  Added crc64 test vectors to crypto/testmgr.c

  Use unaligned le64 macros for setting the result

  Use _GPL for EXPORT_SYMBOL

 crypto/Kconfig                  |   5 ++
 crypto/Makefile                 |   1 +
 crypto/crc64_rocksoft_generic.c |  89 ++++++++++++++++++++++
 crypto/testmgr.c                |   7 ++
 crypto/testmgr.h                |  15 ++++
 include/linux/crc64.h           |   5 ++
 lib/Kconfig                     |   9 +++
 lib/Makefile                    |   1 +
 lib/crc64-rocksoft.c            | 129 ++++++++++++++++++++++++++++++++
 9 files changed, 261 insertions(+)
 create mode 100644 crypto/crc64_rocksoft_generic.c
 create mode 100644 lib/crc64-rocksoft.c

Comments

Vasily Gorbik March 8, 2022, 8:21 p.m. UTC | #1
On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> Hardware specific features may be able to calculate a crc64, so provide
> a framework for drivers to register their implementation. If nothing is
> registered, fallback to the generic table lookup implementation. The
> implementation is modeled after the crct10dif equivalent.

Hi Keith,

this is failing on big-endian systems. I get the following on s390:

[    0.551573] crc32: CRC_LE_BITS = 64, CRC_BE BITS = 64
[    0.551575] crc32: self tests passed, processed 225944 bytes in 118879 nsec
[    0.551697] crc32c: CRC_LE_BITS = 64
[    0.551698] crc32c: self tests passed, processed 112972 bytes in 58963 nsec
[    0.577325] crc32_combine: 8373 self tests passed
[    0.603321] crc32c_combine: 8373 self tests passed
[    0.603502] alg: shash: crc64-rocksoft-generic test failed (wrong result) on test vector 0, cfg="init+update+final aligned buffer"
[    0.603506] ------------[ cut here ]------------
[    0.603507] alg: self-tests for crc64-rocksoft-generic (crc64-rocksoft) failed (rc=-22)
[    0.603542] WARNING: CPU: 0 PID: 43 at crypto/testmgr.c:5726 alg_test+0x3c2/0x638
[    0.603554] Modules linked in:
[    0.603557] CPU: 0 PID: 43 Comm: cryptomgr_test Not tainted 5.17.0-rc7-next-20220308-118584-gcb153b68ff91 #168
[    0.603560] Hardware name: IBM 8561 T01 701 (KVM/Linux)
[    0.603562] Krnl PSW : 0704e00180000000 00000000007d2286 (alg_test+0x3c6/0x638)
[    0.603565]            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3
[    0.603568] Krnl GPRS: 00000000ffffffea 000000000177c350 000000000000004b 00000000ffffefff
[    0.603570]            0000000001663ed0 0000038000000001 0000000001ed1720 0000000081a4e480
[    0.603572]            0000000081a4e400 000003800000003e ffffffffffffffea 000000000000003e
[    0.603611]            0000000081a5a100 000000000000003e 00000000007d2282 00000380001b7cf0
[    0.603618] Krnl Code: 00000000007d2276: c02000495b9e        larl    %r2,00000000010fd9b2
[    0.603618]            00000000007d227c: c0e50026dbc6        brasl   %r14,0000000000cada08
[    0.603618]           #00000000007d2282: af000000            mc      0,0
[    0.603618]           >00000000007d2286: b904002a            lgr     %r2,%r10
[    0.603618]            00000000007d228a: eb6ff1380004        lmg     %r6,%r15,312(%r15)
[    0.603618]            00000000007d2290: 07fe                bcr     15,%r14
[    0.603618]            00000000007d2292: 47000700            bc      0,1792
[    0.603618]            00000000007d2296: 1842                lr      %r4,%r2
[    0.603632] Call Trace:
[    0.603634]  [<00000000007d2286>] alg_test+0x3c6/0x638
[    0.603636] ([<00000000007d2282>] alg_test+0x3c2/0x638)
[    0.603638]  [<00000000007cfff8>] cryptomgr_test+0x68/0x70
[    0.603641]  [<000000000017b228>] kthread+0x108/0x110
[    0.603646]  [<0000000000103374>] __ret_from_fork+0x3c/0x58
[    0.603650]  [<0000000000ccc3ba>] ret_from_fork+0xa/0x40
[    0.603658] Last Breaking-Event-Address:
[    0.603659]  [<0000000000cada68>] __warn_printk+0x60/0x68
[    0.603663] ---[ end trace 0000000000000000 ]---
Keith Busch March 8, 2022, 8:27 p.m. UTC | #2
On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > Hardware specific features may be able to calculate a crc64, so provide
> > a framework for drivers to register their implementation. If nothing is
> > registered, fallback to the generic table lookup implementation. The
> > implementation is modeled after the crct10dif equivalent.
> 
> Hi Keith,
> 
> this is failing on big-endian systems. I get the following on s390:

Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
action. I'll send an update, thank you for the report.
Keith Busch March 8, 2022, 9:46 p.m. UTC | #3
On Tue, Mar 08, 2022 at 12:27:47PM -0800, Keith Busch wrote:
> On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> > On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > > Hardware specific features may be able to calculate a crc64, so provide
> > > a framework for drivers to register their implementation. If nothing is
> > > registered, fallback to the generic table lookup implementation. The
> > > implementation is modeled after the crct10dif equivalent.
> > 
> > Hi Keith,
> > 
> > this is failing on big-endian systems. I get the following on s390:
> 
> Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
> action. I'll send an update, thank you for the report.

I'll set up a BE qemu target this week, but in the meantime, would you
be able to confirm if the following is successful?

---
diff --git a/crypto/crc64_rocksoft_generic.c b/crypto/crc64_rocksoft_generic.c
index 9e812bb26dba..12a8b0575ad1 100644
--- a/crypto/crc64_rocksoft_generic.c
+++ b/crypto/crc64_rocksoft_generic.c
@@ -28,14 +28,14 @@ static int chksum_final(struct shash_desc *desc, u8 *out)
 {
 	u64 *crc = shash_desc_ctx(desc);
 
-	put_unaligned_le64(*crc, out);
+	put_unaligned(*crc, (u64 *)out);
 	return 0;
 }
 
 static int __chksum_finup(u64 crc, const u8 *data, unsigned int len, u8 *out)
 {
 	crc = crc64_rocksoft_generic(crc, data, len);
-	put_unaligned_le64(crc, out);
+	put_unaligned(crc, (u64 *)out);
 	return 0;
 }
 
--
Vasily Gorbik March 8, 2022, 10:03 p.m. UTC | #4
On Tue, Mar 08, 2022 at 01:46:12PM -0800, Keith Busch wrote:
> On Tue, Mar 08, 2022 at 12:27:47PM -0800, Keith Busch wrote:
> > On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> > > On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > > > Hardware specific features may be able to calculate a crc64, so provide
> > > > a framework for drivers to register their implementation. If nothing is
> > > > registered, fallback to the generic table lookup implementation. The
> > > > implementation is modeled after the crct10dif equivalent.
> > > 
> > > Hi Keith,
> > > 
> > > this is failing on big-endian systems. I get the following on s390:
> > 
> > Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
> > action. I'll send an update, thank you for the report.
> 
> I'll set up a BE qemu target this week, but in the meantime, would you
> be able to confirm if the following is successful?

Sure,

[    0.543862] crc32: CRC_LE_BITS = 64, CRC_BE BITS = 64
[    0.543864] crc32: self tests passed, processed 225944 bytes in 118678 nsec
[    0.543986] crc32c: CRC_LE_BITS = 64
[    0.543987] crc32c: self tests passed, processed 112972 bytes in 58932 nsec
[    0.569479] crc32_combine: 8373 self tests passed
[    0.595330] crc32c_combine: 8373 self tests passed

it does the trick. Thanks!
Eric Biggers March 9, 2022, 4:57 a.m. UTC | #5
On Tue, Mar 08, 2022 at 12:27:47PM -0800, Keith Busch wrote:
> On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> > On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > > Hardware specific features may be able to calculate a crc64, so provide
> > > a framework for drivers to register their implementation. If nothing is
> > > registered, fallback to the generic table lookup implementation. The
> > > implementation is modeled after the crct10dif equivalent.
> > 
> > Hi Keith,
> > 
> > this is failing on big-endian systems. I get the following on s390:
> 
> Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
> action. I'll send an update, thank you for the report.

Or you could make the digests in your test vectors have have a consistent byte
order, probably little endian.  That's how "shash" algorithms in the crypto API
normally work, including crc32 and crc32c; they produce bytes as output.  I see
that crct10dif violates that convention, and I assume you copied it from there.
I'm not sure you should do that; crct10dif might be more of a one-off quirk.

- Eric
Keith Busch March 9, 2022, 7:31 p.m. UTC | #6
On Tue, Mar 08, 2022 at 08:57:04PM -0800, Eric Biggers wrote:
> On Tue, Mar 08, 2022 at 12:27:47PM -0800, Keith Busch wrote:
> > On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> > > On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > > > Hardware specific features may be able to calculate a crc64, so provide
> > > > a framework for drivers to register their implementation. If nothing is
> > > > registered, fallback to the generic table lookup implementation. The
> > > > implementation is modeled after the crct10dif equivalent.
> > > 
> > > Hi Keith,
> > > 
> > > this is failing on big-endian systems. I get the following on s390:
> > 
> > Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
> > action. I'll send an update, thank you for the report.
> 
> Or you could make the digests in your test vectors have have a consistent byte
> order, probably little endian.  That's how "shash" algorithms in the crypto API
> normally work, including crc32 and crc32c; they produce bytes as output.  I see
> that crct10dif violates that convention, and I assume you copied it from there.
> I'm not sure you should do that; crct10dif might be more of a one-off quirk.

Right, I started with the t10dif implementation. I changed it to the
unaligned accessor since you indicated the output buffer doesn't have an
alignment guarantee.

Perhaps I'm missing something, but it looks easier to just use the CPU
native endianess here. The only users for t10 and rocksoft transform to
big-endian for the wire protocol at the end, but there's no need to
maintain a specific byte order before setting the payload.
Eric Biggers March 9, 2022, 7:49 p.m. UTC | #7
On Wed, Mar 09, 2022 at 11:31:26AM -0800, Keith Busch wrote:
> On Tue, Mar 08, 2022 at 08:57:04PM -0800, Eric Biggers wrote:
> > On Tue, Mar 08, 2022 at 12:27:47PM -0800, Keith Busch wrote:
> > > On Tue, Mar 08, 2022 at 09:21:41PM +0100, Vasily Gorbik wrote:
> > > > On Thu, Mar 03, 2022 at 12:13:10PM -0800, Keith Busch wrote:
> > > > > Hardware specific features may be able to calculate a crc64, so provide
> > > > > a framework for drivers to register their implementation. If nothing is
> > > > > registered, fallback to the generic table lookup implementation. The
> > > > > implementation is modeled after the crct10dif equivalent.
> > > > 
> > > > Hi Keith,
> > > > 
> > > > this is failing on big-endian systems. I get the following on s390:
> > > 
> > > Oh, I see the put_unaligned_le64() in chksum_final() was not the correct
> > > action. I'll send an update, thank you for the report.
> > 
> > Or you could make the digests in your test vectors have have a consistent byte
> > order, probably little endian.  That's how "shash" algorithms in the crypto API
> > normally work, including crc32 and crc32c; they produce bytes as output.  I see
> > that crct10dif violates that convention, and I assume you copied it from there.
> > I'm not sure you should do that; crct10dif might be more of a one-off quirk.
> 
> Right, I started with the t10dif implementation. I changed it to the
> unaligned accessor since you indicated the output buffer doesn't have an
> alignment guarantee.
> 
> Perhaps I'm missing something, but it looks easier to just use the CPU
> native endianess here. The only users for t10 and rocksoft transform to
> big-endian for the wire protocol at the end, but there's no need to
> maintain a specific byte order before setting the payload.

The issue is that every other "shash" algorithm besides crct10dif, including
crc32 and crc32c, follow the convention of treating the digest as bytes.  Doing
otherwise is unusual for the crypto API.  So I have a slight preference for
treating it as bytes.  Perhaps see what Herbert Xu (maintainer of the crypto
API, Cc'ed) recommends?

- Eric
Keith Busch March 10, 2022, 3:39 p.m. UTC | #8
On Wed, Mar 09, 2022 at 07:49:07PM +0000, Eric Biggers wrote:
> The issue is that every other "shash" algorithm besides crct10dif, including
> crc32 and crc32c, follow the convention of treating the digest as bytes.  Doing
> otherwise is unusual for the crypto API.  So I have a slight preference for
> treating it as bytes.  Perhaps see what Herbert Xu (maintainer of the crypto
> API, Cc'ed) recommends?

I'm okay either way, they're both simple enough. Here is an update atop
this series to match the other shash conventions if this is preferred
over my previous fix:

---
diff --git a/block/t10-pi.c b/block/t10-pi.c
index 914d8cddd43a..f9eb45571bc7 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -282,7 +282,7 @@ EXPORT_SYMBOL(t10_pi_type3_ip);
 
 static __be64 ext_pi_crc64(void *data, unsigned int len)
 {
-	return cpu_to_be64(crc64_rocksoft(data, len));
+	return cpu_to_be64(le64_to_cpu(crc64_rocksoft(data, len)));
 }
 
 static blk_status_t ext_pi_crc64_generate(struct blk_integrity_iter *iter,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index f1a22794c404..f9e5f601c657 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -3686,11 +3686,11 @@ static const struct hash_testvec crc64_rocksoft_tv_template[] = {
 	{
 		.plaintext	= zeroes,
 		.psize		= 4096,
-		.digest		= (u8 *)(u64[]){ 0x6482d367eb22b64eull },
+		.digest		= "\x4e\xb6\x22\xeb\x67\xd3\x82\x64",
 	}, {
 		.plaintext	= ones,
 		.psize		= 4096,
-		.digest		= (u8 *)(u64[]){ 0xc0ddba7302eca3acull },
+		.digest		= "\xac\xa3\xec\x02\x73\xba\xdd\xc0",
 	}
 };
 
diff --git a/include/linux/crc64.h b/include/linux/crc64.h
index e044c60d1e61..5319f9a9fc19 100644
--- a/include/linux/crc64.h
+++ b/include/linux/crc64.h
@@ -12,7 +12,7 @@
 u64 __pure crc64_be(u64 crc, const void *p, size_t len);
 u64 __pure crc64_rocksoft_generic(u64 crc, const void *p, size_t len);
 
-u64 crc64_rocksoft(const unsigned char *buffer, size_t len);
-u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len);
+__le64 crc64_rocksoft(const unsigned char *buffer, size_t len);
+__le64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len);
 
 #endif /* _LINUX_CRC64_H */
diff --git a/lib/crc64-rocksoft.c b/lib/crc64-rocksoft.c
index fc9ae0da5df7..215acb79a15d 100644
--- a/lib/crc64-rocksoft.c
+++ b/lib/crc64-rocksoft.c
@@ -54,16 +54,16 @@ static struct notifier_block crc64_rocksoft_nb = {
 	.notifier_call = crc64_rocksoft_notify,
 };
 
-u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
+__le64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
 {
 	struct {
 		struct shash_desc shash;
-		u64 crc;
+		__le64 crc;
 	} desc;
 	int err;
 
 	if (static_branch_unlikely(&crc64_rocksoft_fallback))
-		return crc64_rocksoft_generic(crc, buffer, len);
+		return cpu_to_le64(crc64_rocksoft_generic(crc, buffer, len));
 
 	rcu_read_lock();
 	desc.shash.tfm = rcu_dereference(crc64_rocksoft_tfm);
@@ -77,7 +77,7 @@ u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
 }
 EXPORT_SYMBOL_GPL(crc64_rocksoft_update);
 
-u64 crc64_rocksoft(const unsigned char *buffer, size_t len)
+__le64 crc64_rocksoft(const unsigned char *buffer, size_t len)
 {
 	return crc64_rocksoft_update(0, buffer, len);
 }
--
Eric Biggers March 10, 2022, 6:36 p.m. UTC | #9
On Thu, Mar 10, 2022 at 07:39:59AM -0800, Keith Busch wrote:
> On Wed, Mar 09, 2022 at 07:49:07PM +0000, Eric Biggers wrote:
> > The issue is that every other "shash" algorithm besides crct10dif, including
> > crc32 and crc32c, follow the convention of treating the digest as bytes.  Doing
> > otherwise is unusual for the crypto API.  So I have a slight preference for
> > treating it as bytes.  Perhaps see what Herbert Xu (maintainer of the crypto
> > API, Cc'ed) recommends?
> 
> I'm okay either way, they're both simple enough. Here is an update atop
> this series to match the other shash conventions if this is preferred
> over my previous fix:
> 
> ---
> diff --git a/block/t10-pi.c b/block/t10-pi.c
> index 914d8cddd43a..f9eb45571bc7 100644
> --- a/block/t10-pi.c
> +++ b/block/t10-pi.c
> @@ -282,7 +282,7 @@ EXPORT_SYMBOL(t10_pi_type3_ip);
>  
>  static __be64 ext_pi_crc64(void *data, unsigned int len)
>  {
> -	return cpu_to_be64(crc64_rocksoft(data, len));
> +	return cpu_to_be64(le64_to_cpu(crc64_rocksoft(data, len)));
>  }
>  
>  static blk_status_t ext_pi_crc64_generate(struct blk_integrity_iter *iter,
> diff --git a/crypto/testmgr.h b/crypto/testmgr.h
> index f1a22794c404..f9e5f601c657 100644
> --- a/crypto/testmgr.h
> +++ b/crypto/testmgr.h
> @@ -3686,11 +3686,11 @@ static const struct hash_testvec crc64_rocksoft_tv_template[] = {
>  	{
>  		.plaintext	= zeroes,
>  		.psize		= 4096,
> -		.digest		= (u8 *)(u64[]){ 0x6482d367eb22b64eull },
> +		.digest		= "\x4e\xb6\x22\xeb\x67\xd3\x82\x64",
>  	}, {
>  		.plaintext	= ones,
>  		.psize		= 4096,
> -		.digest		= (u8 *)(u64[]){ 0xc0ddba7302eca3acull },
> +		.digest		= "\xac\xa3\xec\x02\x73\xba\xdd\xc0",
>  	}
>  };
>  
> diff --git a/include/linux/crc64.h b/include/linux/crc64.h
> index e044c60d1e61..5319f9a9fc19 100644
> --- a/include/linux/crc64.h
> +++ b/include/linux/crc64.h
> @@ -12,7 +12,7 @@
>  u64 __pure crc64_be(u64 crc, const void *p, size_t len);
>  u64 __pure crc64_rocksoft_generic(u64 crc, const void *p, size_t len);
>  
> -u64 crc64_rocksoft(const unsigned char *buffer, size_t len);
> -u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len);
> +__le64 crc64_rocksoft(const unsigned char *buffer, size_t len);
> +__le64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len);
>  
>  #endif /* _LINUX_CRC64_H */
> diff --git a/lib/crc64-rocksoft.c b/lib/crc64-rocksoft.c
> index fc9ae0da5df7..215acb79a15d 100644
> --- a/lib/crc64-rocksoft.c
> +++ b/lib/crc64-rocksoft.c
> @@ -54,16 +54,16 @@ static struct notifier_block crc64_rocksoft_nb = {
>  	.notifier_call = crc64_rocksoft_notify,
>  };
>  
> -u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
> +__le64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
>  {
>  	struct {
>  		struct shash_desc shash;
> -		u64 crc;
> +		__le64 crc;
>  	} desc;
>  	int err;
>  
>  	if (static_branch_unlikely(&crc64_rocksoft_fallback))
> -		return crc64_rocksoft_generic(crc, buffer, len);
> +		return cpu_to_le64(crc64_rocksoft_generic(crc, buffer, len));
>  
>  	rcu_read_lock();
>  	desc.shash.tfm = rcu_dereference(crc64_rocksoft_tfm);
> @@ -77,7 +77,7 @@ u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
>  }
>  EXPORT_SYMBOL_GPL(crc64_rocksoft_update);
>  
> -u64 crc64_rocksoft(const unsigned char *buffer, size_t len)
> +__le64 crc64_rocksoft(const unsigned char *buffer, size_t len)
>  {
>  	return crc64_rocksoft_update(0, buffer, len);
>  }
> --

I think the lib functions should still use native endianness, like what crc32
does.

- Eric
Keith Busch March 11, 2022, 8 p.m. UTC | #10
On Thu, Mar 10, 2022 at 06:36:47PM +0000, Eric Biggers wrote:
> I think the lib functions should still use native endianness, like what crc32
> does.

Gotcha, then it should simply be this patch:

---
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index f1a22794c404..f9e5f601c657 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -3686,11 +3686,11 @@ static const struct hash_testvec crc64_rocksoft_tv_template[] = {
 	{
 		.plaintext	= zeroes,
 		.psize		= 4096,
-		.digest		= (u8 *)(u64[]){ 0x6482d367eb22b64eull },
+		.digest		= "\x4e\xb6\x22\xeb\x67\xd3\x82\x64",
 	}, {
 		.plaintext	= ones,
 		.psize		= 4096,
-		.digest		= (u8 *)(u64[]){ 0xc0ddba7302eca3acull },
+		.digest		= "\xac\xa3\xec\x02\x73\xba\xdd\xc0",
 	}
 };
 
--
diff mbox series

Patch

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 442765219c37..e88e2d00e33d 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -735,6 +735,11 @@  config CRYPTO_CRCT10DIF_VPMSUM
 	  multiply-sum (vpmsum) instructions, introduced in POWER8. Enable on
 	  POWER8 and newer processors for improved performance.
 
+config CRYPTO_CRC64_ROCKSOFT
+	tristate "Rocksoft Model CRC64 algorithm"
+	depends on CRC64
+	select CRYPTO_HASH
+
 config CRYPTO_VPMSUM_TESTER
 	tristate "Powerpc64 vpmsum hardware acceleration tester"
 	depends on CRYPTO_CRCT10DIF_VPMSUM && CRYPTO_CRC32C_VPMSUM
diff --git a/crypto/Makefile b/crypto/Makefile
index d76bff8d0ffd..f754c4d17d6b 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -152,6 +152,7 @@  obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
 obj-$(CONFIG_CRYPTO_CRC32C) += crc32c_generic.o
 obj-$(CONFIG_CRYPTO_CRC32) += crc32_generic.o
 obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_common.o crct10dif_generic.o
+obj-$(CONFIG_CRYPTO_CRC64_ROCKSOFT) += crc64_rocksoft_generic.o
 obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
 obj-$(CONFIG_CRYPTO_LZO) += lzo.o lzo-rle.o
 obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
diff --git a/crypto/crc64_rocksoft_generic.c b/crypto/crc64_rocksoft_generic.c
new file mode 100644
index 000000000000..9e812bb26dba
--- /dev/null
+++ b/crypto/crc64_rocksoft_generic.c
@@ -0,0 +1,89 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/crc64.h>
+#include <linux/module.h>
+#include <crypto/internal/hash.h>
+#include <asm/unaligned.h>
+
+static int chksum_init(struct shash_desc *desc)
+{
+	u64 *crc = shash_desc_ctx(desc);
+
+	*crc = 0;
+
+	return 0;
+}
+
+static int chksum_update(struct shash_desc *desc, const u8 *data,
+			 unsigned int length)
+{
+	u64 *crc = shash_desc_ctx(desc);
+
+	*crc = crc64_rocksoft_generic(*crc, data, length);
+
+	return 0;
+}
+
+static int chksum_final(struct shash_desc *desc, u8 *out)
+{
+	u64 *crc = shash_desc_ctx(desc);
+
+	put_unaligned_le64(*crc, out);
+	return 0;
+}
+
+static int __chksum_finup(u64 crc, const u8 *data, unsigned int len, u8 *out)
+{
+	crc = crc64_rocksoft_generic(crc, data, len);
+	put_unaligned_le64(crc, out);
+	return 0;
+}
+
+static int chksum_finup(struct shash_desc *desc, const u8 *data,
+			unsigned int len, u8 *out)
+{
+	u64 *crc = shash_desc_ctx(desc);
+
+	return __chksum_finup(*crc, data, len, out);
+}
+
+static int chksum_digest(struct shash_desc *desc, const u8 *data,
+			 unsigned int length, u8 *out)
+{
+	return __chksum_finup(0, data, length, out);
+}
+
+static struct shash_alg alg = {
+	.digestsize	= 	sizeof(u64),
+	.init		=	chksum_init,
+	.update		=	chksum_update,
+	.final		=	chksum_final,
+	.finup		=	chksum_finup,
+	.digest		=	chksum_digest,
+	.descsize	=	sizeof(u64),
+	.base		=	{
+		.cra_name		=	CRC64_ROCKSOFT_STRING,
+		.cra_driver_name	=	"crc64-rocksoft-generic",
+		.cra_priority		=	200,
+		.cra_blocksize		=	1,
+		.cra_module		=	THIS_MODULE,
+	}
+};
+
+static int __init crc64_rocksoft_init(void)
+{
+	return crypto_register_shash(&alg);
+}
+
+static void __exit crc64_rocksoft_exit(void)
+{
+	crypto_unregister_shash(&alg);
+}
+
+module_init(crc64_rocksoft_init);
+module_exit(crc64_rocksoft_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Rocksoft model CRC64 calculation.");
+MODULE_ALIAS_CRYPTO("crc64-rocksoft");
+MODULE_ALIAS_CRYPTO("crc64-rocksoft-generic");
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 5831d4bbc64f..2e120eea10b1 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -4526,6 +4526,13 @@  static const struct alg_test_desc alg_test_descs[] = {
 		.suite = {
 			.hash = __VECS(crc32c_tv_template)
 		}
+	}, {
+		.alg = "crc64-rocksoft",
+		.test = alg_test_hash,
+		.fips_allowed = 1,
+		.suite = {
+			.hash = __VECS(crc64_rocksoft_tv_template)
+		}
 	}, {
 		.alg = "crct10dif",
 		.test = alg_test_hash,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index a253d66ba1c1..f1a22794c404 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -3679,6 +3679,21 @@  static const struct hash_testvec rmd160_tv_template[] = {
 	}
 };
 
+static const u8 zeroes[4096] = { [0 ... 4095] = 0 };
+static const u8 ones[4096] = { [0 ... 4095] = 0xff };
+
+static const struct hash_testvec crc64_rocksoft_tv_template[] = {
+	{
+		.plaintext	= zeroes,
+		.psize		= 4096,
+		.digest		= (u8 *)(u64[]){ 0x6482d367eb22b64eull },
+	}, {
+		.plaintext	= ones,
+		.psize		= 4096,
+		.digest		= (u8 *)(u64[]){ 0xc0ddba7302eca3acull },
+	}
+};
+
 static const struct hash_testvec crct10dif_tv_template[] = {
 	{
 		.plaintext	= "abc",
diff --git a/include/linux/crc64.h b/include/linux/crc64.h
index 9480f38cc7cf..e044c60d1e61 100644
--- a/include/linux/crc64.h
+++ b/include/linux/crc64.h
@@ -7,7 +7,12 @@ 
 
 #include <linux/types.h>
 
+#define CRC64_ROCKSOFT_STRING "crc64-rocksoft"
+
 u64 __pure crc64_be(u64 crc, const void *p, size_t len);
 u64 __pure crc64_rocksoft_generic(u64 crc, const void *p, size_t len);
 
+u64 crc64_rocksoft(const unsigned char *buffer, size_t len);
+u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len);
+
 #endif /* _LINUX_CRC64_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index c80fde816a7e..da3e03579666 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -146,6 +146,15 @@  config CRC_T10DIF
 	  kernel tree needs to calculate CRC checks for use with the
 	  SCSI data integrity subsystem.
 
+config CRC64_ROCKSOFT
+	tristate "CRC calculation for the Rocksoft model CRC64"
+	select CRC64
+	select CRYPTO
+	select CRYPTO_CRC64_ROCKSOFT
+	help
+	  This option provides a CRC64 API to a registered crypto driver.
+	  This is used with the block layer's data integrity subsystem.
+
 config CRC_ITU_T
 	tristate "CRC ITU-T V.41 functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 300f569c626b..7f7ae7458b6c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -175,6 +175,7 @@  obj-$(CONFIG_CRC4)	+= crc4.o
 obj-$(CONFIG_CRC7)	+= crc7.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
 obj-$(CONFIG_CRC8)	+= crc8.o
+obj-$(CONFIG_CRC64_ROCKSOFT) += crc64-rocksoft.o
 obj-$(CONFIG_XXHASH)	+= xxhash.o
 obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
 
diff --git a/lib/crc64-rocksoft.c b/lib/crc64-rocksoft.c
new file mode 100644
index 000000000000..55d32872778a
--- /dev/null
+++ b/lib/crc64-rocksoft.c
@@ -0,0 +1,126 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc64.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <crypto/hash.h>
+#include <crypto/algapi.h>
+#include <linux/static_key.h>
+#include <linux/notifier.h>
+
+static struct crypto_shash __rcu *crc64_rocksoft_tfm;
+static DEFINE_STATIC_KEY_TRUE(crc64_rocksoft_fallback);
+static DEFINE_MUTEX(crc64_rocksoft_mutex);
+static struct work_struct crc64_rocksoft_rehash_work;
+
+static int crc64_rocksoft_notify(struct notifier_block *self, unsigned long val, void *data)
+{
+	struct crypto_alg *alg = data;
+
+	if (val != CRYPTO_MSG_ALG_LOADED ||
+	    strcmp(alg->cra_name, CRC64_ROCKSOFT_STRING))
+		return NOTIFY_DONE;
+
+	schedule_work(&crc64_rocksoft_rehash_work);
+	return NOTIFY_OK;
+}
+
+static void crc64_rocksoft_rehash(struct work_struct *work)
+{
+	struct crypto_shash *new, *old;
+
+	mutex_lock(&crc64_rocksoft_mutex);
+	old = rcu_dereference_protected(crc64_rocksoft_tfm,
+					lockdep_is_held(&crc64_rocksoft_mutex));
+	new = crypto_alloc_shash(CRC64_ROCKSOFT_STRING, 0, 0);
+	if (IS_ERR(new)) {
+		mutex_unlock(&crc64_rocksoft_mutex);
+		return;
+	}
+	rcu_assign_pointer(crc64_rocksoft_tfm, new);
+	mutex_unlock(&crc64_rocksoft_mutex);
+
+	if (old) {
+		synchronize_rcu();
+		crypto_free_shash(old);
+	} else {
+		static_branch_disable(&crc64_rocksoft_fallback);
+	}
+}
+
+static struct notifier_block crc64_rocksoft_nb = {
+	.notifier_call = crc64_rocksoft_notify,
+};
+
+u64 crc64_rocksoft_update(u64 crc, const unsigned char *buffer, size_t len)
+{
+	struct {
+		struct shash_desc shash;
+		u64 crc;
+	} desc;
+	int err;
+
+	if (static_branch_unlikely(&crc64_rocksoft_fallback))
+		return crc64_rocksoft_generic(crc, buffer, len);
+
+	rcu_read_lock();
+	desc.shash.tfm = rcu_dereference(crc64_rocksoft_tfm);
+	desc.crc = crc;
+	err = crypto_shash_update(&desc.shash, buffer, len);
+	rcu_read_unlock();
+
+	BUG_ON(err);
+
+	return desc.crc;
+}
+EXPORT_SYMBOL_GPL(crc64_rocksoft_update);
+
+u64 crc64_rocksoft(const unsigned char *buffer, size_t len)
+{
+	return crc64_rocksoft_update(0, buffer, len);
+}
+EXPORT_SYMBOL_GPL(crc64_rocksoft);
+
+static int __init crc64_rocksoft_mod_init(void)
+{
+	INIT_WORK(&crc64_rocksoft_rehash_work, crc64_rocksoft_rehash);
+	crypto_register_notifier(&crc64_rocksoft_nb);
+	crc64_rocksoft_rehash(&crc64_rocksoft_rehash_work);
+	return 0;
+}
+
+static void __exit crc64_rocksoft_mod_fini(void)
+{
+	crypto_unregister_notifier(&crc64_rocksoft_nb);
+	cancel_work_sync(&crc64_rocksoft_rehash_work);
+	crypto_free_shash(rcu_dereference_protected(crc64_rocksoft_tfm, 1));
+}
+
+module_init(crc64_rocksoft_mod_init);
+module_exit(crc64_rocksoft_mod_fini);
+
+static int crc64_rocksoft_transform_show(char *buffer, const struct kernel_param *kp)
+{
+	struct crypto_shash *tfm;
+	int len;
+
+	if (static_branch_unlikely(&crc64_rocksoft_fallback))
+		return sprintf(buffer, "fallback\n");
+
+	rcu_read_lock();
+	tfm = rcu_dereference(crc64_rocksoft_tfm);
+	len = snprintf(buffer, PAGE_SIZE, "%s\n",
+		       crypto_shash_driver_name(tfm));
+	rcu_read_unlock();
+
+	return len;
+}
+
+module_param_call(transform, NULL, crc64_rocksoft_transform_show, NULL, 0444);
+
+MODULE_AUTHOR("Keith Busch <kbusch@kernel.org>");
+MODULE_DESCRIPTION("Rocksoft model CRC64 calculation (library API)");
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: crc64");