Re: Re: PING: [PATCH] virtio-blk: fix implicit overflow on virtio_max_dma_size

2023-10-19 Thread zhenwei pi via Virtualization

Cc Paolo, Stefan, Xuan and linux-block.

On 10/19/23 17:52, Michael S. Tsirkin wrote:

On Thu, Oct 19, 2023 at 05:43:55PM +0800, zhenwei pi wrote:

Hi Michael,

This seems to have been ignored as you suggested.

LINK: https://www.spinics.net/lists/linux-virtualization/msg63015.html


Pls Cc more widely then:

Paolo Bonzini  (reviewer:VIRTIO BLOCK AND SCSI DRIVERS)
Stefan Hajnoczi  (reviewer:VIRTIO BLOCK AND SCSI DRIVERS)
Xuan Zhuo  (reviewer:VIRTIO CORE AND NET DRIVERS)
Jens Axboe  (maintainer:BLOCK LAYER)
linux-bl...@vger.kernel.org (open list:BLOCK LAYER)

would all be good people to ask to review this.



On 9/4/23 14:10, zhenwei pi wrote:

The following codes have an implicit conversion from size_t to u32:
(u32)max_size = (size_t)virtio_max_dma_size(vdev);

This may lead overflow, Ex (size_t)4G -> (u32)0. Once
virtio_max_dma_size() has a larger size than U32_MAX, use U32_MAX
instead.

Signed-off-by: zhenwei pi 
---
   drivers/block/virtio_blk.c | 4 +++-
   1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 1fe011676d07..4a4b9bad551e 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1313,6 +1313,7 @@ static int virtblk_probe(struct virtio_device *vdev)
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
unsigned int queue_depth;
+   size_t max_dma_size;
if (!vdev->config->get) {
dev_err(>dev, "%s failure: config access disabled\n",
@@ -1411,7 +1412,8 @@ static int virtblk_probe(struct virtio_device *vdev)
/* No real sector limit. */
blk_queue_max_hw_sectors(q, UINT_MAX);
-   max_size = virtio_max_dma_size(vdev);
+   max_dma_size = virtio_max_dma_size(vdev);
+   max_size = max_dma_size > U32_MAX ? U32_MAX : max_dma_size;
/* Host can optionally specify maximum segment size and number of
 * segments. */


--
zhenwei pi




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


PING: [PATCH] virtio-blk: fix implicit overflow on virtio_max_dma_size

2023-10-19 Thread zhenwei pi via Virtualization

Hi Michael,

This seems to have been ignored as you suggested.

LINK: https://www.spinics.net/lists/linux-virtualization/msg63015.html

On 9/4/23 14:10, zhenwei pi wrote:

The following codes have an implicit conversion from size_t to u32:
(u32)max_size = (size_t)virtio_max_dma_size(vdev);

This may lead overflow, Ex (size_t)4G -> (u32)0. Once
virtio_max_dma_size() has a larger size than U32_MAX, use U32_MAX
instead.

Signed-off-by: zhenwei pi 
---
  drivers/block/virtio_blk.c | 4 +++-
  1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 1fe011676d07..4a4b9bad551e 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1313,6 +1313,7 @@ static int virtblk_probe(struct virtio_device *vdev)
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
unsigned int queue_depth;
+   size_t max_dma_size;
  
  	if (!vdev->config->get) {

dev_err(>dev, "%s failure: config access disabled\n",
@@ -1411,7 +1412,8 @@ static int virtblk_probe(struct virtio_device *vdev)
/* No real sector limit. */
blk_queue_max_hw_sectors(q, UINT_MAX);
  
-	max_size = virtio_max_dma_size(vdev);

+   max_dma_size = virtio_max_dma_size(vdev);
+   max_size = max_dma_size > U32_MAX ? U32_MAX : max_dma_size;
  
  	/* Host can optionally specify maximum segment size and number of

 * segments. */


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH] crypto: virtio-crypto: call finalize with bh disabled

2023-09-27 Thread zhenwei pi via Virtualization

Hi Michael & Lei,

I volunteer to fix this by workqueue.

I also notice that device drivers use workqueue to handle config-changed 
again and again, what about re-implement __virtio_config_changed() by 
kicking workqueue instead?


By the way, balloon dirvers uses 
spin_lock_irqsave/spin_unlock_irqrestore in config-changed callback, do 
it handle correctly?


On 9/27/23 21:25, Halil Pasic wrote:

On Wed, 27 Sep 2023 09:24:09 +
"Gonglei (Arei)"  wrote:


On a related note, config change callback is also handled incorrectly in this
driver, it takes a mutex from interrupt context.


Good catch. Will fix it.


Thanks Gonglei! Sorry I first misunderstood this as a problem within the
virtio-ccw driver, but it is actually about virtio-crypto. Thanks for
fixing this!

Regards,
Halil


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH] virtio-blk: fix implicit overflow on virtio_max_dma_size

2023-09-04 Thread zhenwei pi via Virtualization
The following codes have an implicit conversion from size_t to u32:
(u32)max_size = (size_t)virtio_max_dma_size(vdev);

This may lead overflow, Ex (size_t)4G -> (u32)0. Once
virtio_max_dma_size() has a larger size than U32_MAX, use U32_MAX
instead.

Signed-off-by: zhenwei pi 
---
 drivers/block/virtio_blk.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 1fe011676d07..4a4b9bad551e 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1313,6 +1313,7 @@ static int virtblk_probe(struct virtio_device *vdev)
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
unsigned int queue_depth;
+   size_t max_dma_size;
 
if (!vdev->config->get) {
dev_err(>dev, "%s failure: config access disabled\n",
@@ -1411,7 +1412,8 @@ static int virtblk_probe(struct virtio_device *vdev)
/* No real sector limit. */
blk_queue_max_hw_sectors(q, UINT_MAX);
 
-   max_size = virtio_max_dma_size(vdev);
+   max_dma_size = virtio_max_dma_size(vdev);
+   max_size = max_dma_size > U32_MAX ? U32_MAX : max_dma_size;
 
/* Host can optionally specify maximum segment size and number of
 * segments. */
-- 
2.34.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH] virtio_ring: use u32 for virtio_max_dma_size

2023-07-04 Thread zhenwei pi via Virtualization




On 7/4/23 14:21, Michael S. Tsirkin wrote:

On Wed, May 10, 2023 at 10:54:37AM +0800, zhenwei pi wrote:

Both split ring and packed ring use 32bits to describe the length of
a descriptor: see struct vring_desc and struct vring_packed_desc.
This means the max segment size supported by virtio is U32_MAX.

An example of virtio_max_dma_size in virtio_blk.c:
   u32 v, max_size;

   max_size = virtio_max_dma_size(vdev);  -> implicit convert
   err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
  struct virtio_blk_config, size_max, );
   max_size = min(max_size, v);

There is a risk during implicit convert here, once virtio_max_dma_size
returns 4G, max_size becomes 0.

Fixes: e6d6dd6c875e ("virtio: Introduce virtio_max_dma_size()")
Cc: Joerg Roedel 
Signed-off-by: zhenwei pi 
---
  drivers/virtio/virtio_ring.c | 12 
  include/linux/virtio.h   |  2 +-
  2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..55cfecf030a1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -289,12 +289,16 @@ static bool vring_use_dma_api(const struct virtio_device 
*vdev)
return false;
  }
  
-size_t virtio_max_dma_size(const struct virtio_device *vdev)

+u32 virtio_max_dma_size(const struct virtio_device *vdev)
  {
-   size_t max_segment_size = SIZE_MAX;
+   u32 max_segment_size = U32_MAX;
  
-	if (vring_use_dma_api(vdev))

-   max_segment_size = dma_max_mapping_size(vdev->dev.parent);
+   if (vring_use_dma_api(vdev)) {
+   size_t max_dma_size = dma_max_mapping_size(vdev->dev.parent);
+
+   if (max_dma_size < max_segment_size)
+   max_segment_size = max_dma_size;
+   }
  
  	return max_segment_size;

  }


Took a while for me to get this, it's confusing.  I think the issue is
really in virtio blk, so I would just change max_size there to size_t
and be done with it.




Fine.





diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index b93238db94e3..1a605f408329 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -147,7 +147,7 @@ int virtio_device_restore(struct virtio_device *dev);
  #endif
  void virtio_reset_device(struct virtio_device *dev);
  
-size_t virtio_max_dma_size(const struct virtio_device *vdev);

+u32 virtio_max_dma_size(const struct virtio_device *vdev);
  
  #define virtio_device_for_each_vq(vdev, vq) \

list_for_each_entry(vq, >vqs, list)
--
2.20.1




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


PING Re: [PATCH] virtio_ring: use u32 for virtio_max_dma_size

2023-07-03 Thread zhenwei pi via Virtualization

Hi Michael,

This seems to be ignored...

On 5/10/23 12:06, Michael S. Tsirkin wrote:

On Wed, May 10, 2023 at 12:04:50PM +0800, Jason Wang wrote:

On Wed, May 10, 2023 at 11:44 AM Michael S. Tsirkin  wrote:


On Wed, May 10, 2023 at 11:26:54AM +0800, Xuan Zhuo wrote:

On Wed, 10 May 2023 10:54:37 +0800, zhenwei pi  wrote:

Both split ring and packed ring use 32bits to describe the length of
a descriptor: see struct vring_desc and struct vring_packed_desc.
This means the max segment size supported by virtio is U32_MAX.

An example of virtio_max_dma_size in virtio_blk.c:
   u32 v, max_size;

   max_size = virtio_max_dma_size(vdev);  -> implicit convert
   err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
  struct virtio_blk_config, size_max, );
   max_size = min(max_size, v);

There is a risk during implicit convert here, once virtio_max_dma_size
returns 4G, max_size becomes 0.

Fixes: e6d6dd6c875e ("virtio: Introduce virtio_max_dma_size()")
Cc: Joerg Roedel 
Signed-off-by: zhenwei pi 
---
  drivers/virtio/virtio_ring.c | 12 
  include/linux/virtio.h   |  2 +-
  2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..55cfecf030a1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -289,12 +289,16 @@ static bool vring_use_dma_api(const struct virtio_device 
*vdev)
 return false;
  }

-size_t virtio_max_dma_size(const struct virtio_device *vdev)
+u32 virtio_max_dma_size(const struct virtio_device *vdev)



LGTM

But, should we change the parameter to vq, then use the dma_dev?

@Jason

Thanks.




that would be an unrelated rework.


Probably, but I think it's better to be done on top otherwise we may forget.

Thanks


Just to make things clear I'm merging fixes for this
release but cleanups belong in the next one.




  {
-   size_t max_segment_size = SIZE_MAX;
+   u32 max_segment_size = U32_MAX;

-   if (vring_use_dma_api(vdev))
-   max_segment_size = dma_max_mapping_size(vdev->dev.parent);
+   if (vring_use_dma_api(vdev)) {
+   size_t max_dma_size = dma_max_mapping_size(vdev->dev.parent);
+
+   if (max_dma_size < max_segment_size)
+   max_segment_size = max_dma_size;
+   }

 return max_segment_size;
  }
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index b93238db94e3..1a605f408329 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -147,7 +147,7 @@ int virtio_device_restore(struct virtio_device *dev);
  #endif
  void virtio_reset_device(struct virtio_device *dev);

-size_t virtio_max_dma_size(const struct virtio_device *vdev);
+u32 virtio_max_dma_size(const struct virtio_device *vdev);

  #define virtio_device_for_each_vq(vdev, vq) \
 list_for_each_entry(vq, >vqs, list)
--
2.20.1







--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Re: Re: Re: Re: [PATCH v2 1/2] virtio: abstract virtqueue related methods

2023-05-18 Thread zhenwei pi via Virtualization




On 5/18/23 18:09, Michael S. Tsirkin wrote:

On Thu, May 18, 2023 at 08:47:22AM +0800, zhenwei pi wrote:

On 5/17/23 18:39, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 04:35:55PM +0800, zhenwei pi wrote:



On 5/17/23 15:46, Christoph Hellwig wrote:

On Wed, May 17, 2023 at 03:43:03PM +0800, zhenwei pi wrote:

I have a plan to introduce 'Virtio Over Fabrics'(TCP) as Virtio
transport, as mentioned in cover letter of this series:
3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html


Just don't do it.  Please define your own protocols over RDMA or TCP
for exactly the operations you need (for many they will already exist)
instead of piggyg backing on virtio and making everyone else pay the
price.



Hi

1, `virtqueue_add_inbuf` in current version:
static inline int virtqueue_add_inbuf(struct virtqueue *vq,
struct scatterlist *sg,
unsigned int num,
void *data,
gfp_t gfp)
{
  if (likely(!vq->abstract))
  return vring_virtqueue_add_sgs(vq, , num, 0, 1, data,
NULL, gfp);

  return vq->add_sgs(vq, , num, 0, 1, data, NULL, gfp);
}

And disassemble 'virtinput_queue_evtbuf':
static void virtinput_queue_evtbuf(struct virtio_input *vi,
 struct virtio_input_event *evtbuf)
{
  struct scatterlist sg[1];

  sg_init_one(sg, evtbuf, sizeof(*evtbuf));
  virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
}

I notice that two instructions are newly added for vring like:
   24d:   80 78 35 00 cmpb   $0x0,0x35(%rax)
   251:   75 3f   jne292

Is it an expensive price...


Can we somehow only override the kick method?
Then take the ring and send it over ...



Could you please take a look at this code?
https://github.com/pizhenwei/linux/blob/virtio-of-github/drivers/virtio/virtio_fabrics.c#LL861C13-L861C23


what am I looking at here?

Looks like at least vof_handle_vq duplicates some code from vringh.
But besides that yes, that's the idea.



OK, I'd drop this series.

Cc Jason & Stefan.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: Re: [PATCH v2 1/2] virtio: abstract virtqueue related methods

2023-05-17 Thread zhenwei pi via Virtualization

On 5/17/23 18:39, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 04:35:55PM +0800, zhenwei pi wrote:



On 5/17/23 15:46, Christoph Hellwig wrote:

On Wed, May 17, 2023 at 03:43:03PM +0800, zhenwei pi wrote:

I have a plan to introduce 'Virtio Over Fabrics'(TCP) as Virtio
transport, as mentioned in cover letter of this series:
3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html


Just don't do it.  Please define your own protocols over RDMA or TCP
for exactly the operations you need (for many they will already exist)
instead of piggyg backing on virtio and making everyone else pay the
price.



Hi

1, `virtqueue_add_inbuf` in current version:
static inline int virtqueue_add_inbuf(struct virtqueue *vq,
   struct scatterlist *sg,
   unsigned int num,
   void *data,
   gfp_t gfp)
{
 if (likely(!vq->abstract))
 return vring_virtqueue_add_sgs(vq, , num, 0, 1, data,
NULL, gfp);

 return vq->add_sgs(vq, , num, 0, 1, data, NULL, gfp);
}

And disassemble 'virtinput_queue_evtbuf':
static void virtinput_queue_evtbuf(struct virtio_input *vi,
struct virtio_input_event *evtbuf)
{
 struct scatterlist sg[1];

 sg_init_one(sg, evtbuf, sizeof(*evtbuf));
 virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
}

I notice that two instructions are newly added for vring like:
  24d:   80 78 35 00 cmpb   $0x0,0x35(%rax)
  251:   75 3f   jne292

Is it an expensive price...


Can we somehow only override the kick method?
Then take the ring and send it over ...



Could you please take a look at this code?
https://github.com/pizhenwei/linux/blob/virtio-of-github/drivers/virtio/virtio_fabrics.c#LL861C13-L861C23




2, Storage/FS specific remote protocol is quite popular, otherwise I'm not
familiar with other device protocols. For example, I need a remote crypto
device to accelerate HTTPS ... With Virtio Over Fabrics, I have a chance to
attach a virtio-crypto device to do this work.

--
zhenwei pi




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH v2 1/2] virtio: abstract virtqueue related methods

2023-05-17 Thread zhenwei pi via Virtualization




On 5/17/23 15:46, Christoph Hellwig wrote:

On Wed, May 17, 2023 at 03:43:03PM +0800, zhenwei pi wrote:

I have a plan to introduce 'Virtio Over Fabrics'(TCP) as Virtio
transport, as mentioned in cover letter of this series:
3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html


Just don't do it.  Please define your own protocols over RDMA or TCP
for exactly the operations you need (for many they will already exist)
instead of piggyg backing on virtio and making everyone else pay the
price.



Hi

1, `virtqueue_add_inbuf` in current version:
static inline int virtqueue_add_inbuf(struct virtqueue *vq,
  struct scatterlist *sg,
  unsigned int num,
  void *data,
  gfp_t gfp)
{
if (likely(!vq->abstract))
return vring_virtqueue_add_sgs(vq, , num, 0, 1, 
data, NULL, gfp);


return vq->add_sgs(vq, , num, 0, 1, data, NULL, gfp);
}

And disassemble 'virtinput_queue_evtbuf':
static void virtinput_queue_evtbuf(struct virtio_input *vi,
   struct virtio_input_event *evtbuf)
{
struct scatterlist sg[1];

sg_init_one(sg, evtbuf, sizeof(*evtbuf));
virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
}

I notice that two instructions are newly added for vring like:
 24d:   80 78 35 00 cmpb   $0x0,0x35(%rax)
 251:   75 3f   jne292

Is it an expensive price...

2, Storage/FS specific remote protocol is quite popular, otherwise I'm 
not familiar with other device protocols. For example, I need a remote 
crypto device to accelerate HTTPS ... With Virtio Over Fabrics, I have a 
chance to attach a virtio-crypto device to do this work.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH v2 1/2] virtio: abstract virtqueue related methods

2023-05-17 Thread zhenwei pi via Virtualization




On 5/17/23 15:39, Christoph Hellwig wrote:

On Wed, May 17, 2023 at 10:54:23AM +0800, zhenwei pi wrote:

All the vring based virtqueue methods could be abstratct in theory,
MST suggested that add/get bufs and kick functions are quite perfmance
sensitive, so export these functions from virtio_ring.ko, drivers
still call them in a fast path.


Who is going to use this?  And why do you think every virtio users
would want to pay for indirect calls just for your shiny new feature?

This seems like an amazingly bad idea to me.


Hi,

I have a plan to introduce 'Virtio Over Fabrics'(TCP) as Virtio 
transport, as mentioned in cover letter of this series:

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: Re: Re: [PATCH v2 0/2] virtio: abstract virtqueue related methods

2023-05-17 Thread zhenwei pi via Virtualization

On 5/17/23 14:10, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 12:58:10PM +0800, zhenwei pi wrote:

On 5/17/23 11:57, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 11:51:03AM +0800, zhenwei pi wrote:



On 5/17/23 11:46, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 10:54:22AM +0800, zhenwei pi wrote:

v1 -> v2:
- Suggested by MST, use fast path for vring based performance
sensitive API.
- Reduce changes in tools/virtio.

Add test result(no obvious change):
Before:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036893, pinged 68278
Host: notified 68278, pinged 3093532

real0m14.463s
user0m6.437s
sys 0m8.010s

After:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036709, pinged 68347
Host: notified 68347, pinged 3085292

real0m14.196s
user0m6.289s
sys 0m7.885s

v1:
Hi,

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

Then I try to abstract virtqueue related methods in this series, the
details changes see the comment of patch 'virtio: abstract virtqueue related 
methods'.

Something is still remained:
- __virtqueue_break/__virtqueue_unbreak is supposed to use by internal
 virtio core, I'd like to rename them to vring_virtqueue_break
 /vring_virtqueue_unbreak. Is this reasonable?


Why? These just set a flag?



Rename '__virtqueue_break' to 'vring_virtqueue_break', to make symbols
exported from virtio_ring.ko have unified prefix 'vring_virtqueue_xxx'.


I just do not see why you need these callbacks at all.



I use these callbacks for break/unbreak device like:
static inline void virtio_break_device(struct virtio_device *dev)
{
struct virtqueue *vq;

spin_lock(>vqs_list_lock);
list_for_each_entry(vq, >vqs, list) {
vq->__break(vq);
}
spin_unlock(>vqs_list_lock);
}


why do this? backend knows they are broken.



I grep 'virtio_break_device' in the latest code:
arch/um/drivers/virtio_uml.c:1147:  virtio_break_device(_dev->vdev);
arch/um/drivers/virtio_uml.c:1285:  virtio_break_device(_dev->vdev);
drivers/crypto/virtio/virtio_crypto_core.c:269:	 
virtio_break_device(vcrypto->vdev);

drivers/s390/virtio/virtio_ccw.c:1251:  
virtio_break_device(>vdev);
drivers/s390/virtio/virtio_ccw.c:1268:  
virtio_break_device(>vdev);
drivers/firmware/arm_scmi/virtio.c:489: 
virtio_break_device(vioch->vqueue->vdev);

drivers/char/virtio_console.c:1956: virtio_break_device(vdev);

Some virtio drivers use 'virtio_break_device'...


- virtqueue_get_desc_addr/virtqueue_get_avail_addr/virtqueue_get_used_addr
 /virtqueue_get_vring is vring specific, I'd like to rename them like
 vring_virtqueue_get_desc_addr. Is this reasonable?
- there are still some functions in virtio_ring.c with prefix *virtqueue*,
 for example 'virtqueue_add_split', just keep it or rename it to
 'vring_virtqueue_add_split'?
zhenwei pi (2):
 virtio: abstract virtqueue related methods
 tools/virtio: implement virtqueue in test

drivers/virtio/virtio_ring.c | 285 +-
include/linux/virtio.h   | 441 +++
include/linux/virtio_ring.h  |  26 +++
tools/virtio/linux/virtio.h  | 355 +---
4 files changed, 807 insertions(+), 300 deletions(-)

--
2.20.1




--
zhenwei pi




--
zhenwei pi




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: Re: [PATCH v2 0/2] virtio: abstract virtqueue related methods

2023-05-16 Thread zhenwei pi via Virtualization

On 5/17/23 11:57, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 11:51:03AM +0800, zhenwei pi wrote:



On 5/17/23 11:46, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 10:54:22AM +0800, zhenwei pi wrote:

v1 -> v2:
- Suggested by MST, use fast path for vring based performance
sensitive API.
- Reduce changes in tools/virtio.

Add test result(no obvious change):
Before:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036893, pinged 68278
Host: notified 68278, pinged 3093532

real0m14.463s
user0m6.437s
sys 0m8.010s

After:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036709, pinged 68347
Host: notified 68347, pinged 3085292

real0m14.196s
user0m6.289s
sys 0m7.885s

v1:
Hi,

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

Then I try to abstract virtqueue related methods in this series, the
details changes see the comment of patch 'virtio: abstract virtqueue related 
methods'.

Something is still remained:
- __virtqueue_break/__virtqueue_unbreak is supposed to use by internal
virtio core, I'd like to rename them to vring_virtqueue_break
/vring_virtqueue_unbreak. Is this reasonable?


Why? These just set a flag?



Rename '__virtqueue_break' to 'vring_virtqueue_break', to make symbols
exported from virtio_ring.ko have unified prefix 'vring_virtqueue_xxx'.


I just do not see why you need these callbacks at all.



I use these callbacks for break/unbreak device like:
static inline void virtio_break_device(struct virtio_device *dev)
{
struct virtqueue *vq;

spin_lock(>vqs_list_lock);
list_for_each_entry(vq, >vqs, list) {
vq->__break(vq);
}
spin_unlock(>vqs_list_lock);
}


- virtqueue_get_desc_addr/virtqueue_get_avail_addr/virtqueue_get_used_addr
/virtqueue_get_vring is vring specific, I'd like to rename them like
vring_virtqueue_get_desc_addr. Is this reasonable?
- there are still some functions in virtio_ring.c with prefix *virtqueue*,
for example 'virtqueue_add_split', just keep it or rename it to
'vring_virtqueue_add_split'?
zhenwei pi (2):
virtio: abstract virtqueue related methods
tools/virtio: implement virtqueue in test

   drivers/virtio/virtio_ring.c | 285 +-
   include/linux/virtio.h   | 441 +++
   include/linux/virtio_ring.h  |  26 +++
   tools/virtio/linux/virtio.h  | 355 +---
   4 files changed, 807 insertions(+), 300 deletions(-)

--
2.20.1




--
zhenwei pi




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH v2 0/2] virtio: abstract virtqueue related methods

2023-05-16 Thread zhenwei pi via Virtualization




On 5/17/23 11:46, Michael S. Tsirkin wrote:

On Wed, May 17, 2023 at 10:54:22AM +0800, zhenwei pi wrote:

v1 -> v2:
- Suggested by MST, use fast path for vring based performance
sensitive API.
- Reduce changes in tools/virtio.

Add test result(no obvious change):
Before:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036893, pinged 68278
Host: notified 68278, pinged 3093532

real0m14.463s
user0m6.437s
sys 0m8.010s

After:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036709, pinged 68347
Host: notified 68347, pinged 3085292

real0m14.196s
user0m6.289s
sys 0m7.885s

v1:
Hi,

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

Then I try to abstract virtqueue related methods in this series, the
details changes see the comment of patch 'virtio: abstract virtqueue related 
methods'.

Something is still remained:
- __virtqueue_break/__virtqueue_unbreak is supposed to use by internal
   virtio core, I'd like to rename them to vring_virtqueue_break
   /vring_virtqueue_unbreak. Is this reasonable?


Why? These just set a flag?



Rename '__virtqueue_break' to 'vring_virtqueue_break', to make symbols 
exported from virtio_ring.ko have unified prefix 'vring_virtqueue_xxx'.



- virtqueue_get_desc_addr/virtqueue_get_avail_addr/virtqueue_get_used_addr
   /virtqueue_get_vring is vring specific, I'd like to rename them like
   vring_virtqueue_get_desc_addr. Is this reasonable?
- there are still some functions in virtio_ring.c with prefix *virtqueue*,
   for example 'virtqueue_add_split', just keep it or rename it to
   'vring_virtqueue_add_split'?
zhenwei pi (2):
   virtio: abstract virtqueue related methods
   tools/virtio: implement virtqueue in test

  drivers/virtio/virtio_ring.c | 285 +-
  include/linux/virtio.h   | 441 +++
  include/linux/virtio_ring.h  |  26 +++
  tools/virtio/linux/virtio.h  | 355 +---
  4 files changed, 807 insertions(+), 300 deletions(-)

--
2.20.1




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v2 0/2] virtio: abstract virtqueue related methods

2023-05-16 Thread zhenwei pi via Virtualization
v1 -> v2:
- Suggested by MST, use fast path for vring based performance
sensitive API.
- Reduce changes in tools/virtio.

Add test result(no obvious change):
Before:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036893, pinged 68278
Host: notified 68278, pinged 3093532

real0m14.463s
user0m6.437s
sys 0m8.010s

After:
time ./vringh_test --parallel
Using CPUS 0 and 191
Guest: notified 10036709, pinged 68347
Host: notified 68347, pinged 3085292

real0m14.196s
user0m6.289s
sys 0m7.885s

v1:
Hi,

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

Then I try to abstract virtqueue related methods in this series, the
details changes see the comment of patch 'virtio: abstract virtqueue related 
methods'.

Something is still remained:
- __virtqueue_break/__virtqueue_unbreak is supposed to use by internal
  virtio core, I'd like to rename them to vring_virtqueue_break
  /vring_virtqueue_unbreak. Is this reasonable?
- virtqueue_get_desc_addr/virtqueue_get_avail_addr/virtqueue_get_used_addr
  /virtqueue_get_vring is vring specific, I'd like to rename them like
  vring_virtqueue_get_desc_addr. Is this reasonable?
- there are still some functions in virtio_ring.c with prefix *virtqueue*,
  for example 'virtqueue_add_split', just keep it or rename it to
  'vring_virtqueue_add_split'?
zhenwei pi (2):
  virtio: abstract virtqueue related methods
  tools/virtio: implement virtqueue in test

 drivers/virtio/virtio_ring.c | 285 +-
 include/linux/virtio.h   | 441 +++
 include/linux/virtio_ring.h  |  26 +++
 tools/virtio/linux/virtio.h  | 355 +---
 4 files changed, 807 insertions(+), 300 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v2 1/2] virtio: abstract virtqueue related methods

2023-05-16 Thread zhenwei pi via Virtualization
There is already a virtqueue abstract structure in virtio subsystem
(see struct virtqueue in include/linux/virtio.h), however the vring
based virtqueue is used only in the past years, the virtqueue related
methods mix much with vring(just look like the codes, virtqueue_xxx
functions are implemented in virtio_ring.c).

Abstract virtqueue related methods(see struct virtqueue_ops), and
separate virtqueue_xxx symbols from vring. This allows a non-vring
based transport in the future. See changes in virtio.h.

All the vring based virtqueue methods could be abstratct in theory,
MST suggested that add/get bufs and kick functions are quite perfmance
sensitive, so export these functions from virtio_ring.ko, drivers
still call them in a fast path.

Cc: Stefan Hajnoczi 
Signed-off-by: zhenwei pi 
---
 drivers/virtio/virtio_ring.c | 285 +-
 include/linux/virtio.h   | 441 +++
 include/linux/virtio_ring.h  |  26 +++
 3 files changed, 483 insertions(+), 269 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..82c26f50d941 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -226,6 +226,7 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int 
index,
   struct device *dma_dev);
 static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num);
 static void vring_free(struct virtqueue *_vq);
+static void vring_virtqueue_set_ops(struct virtqueue *vq);
 
 /*
  * Helpers.
@@ -2036,6 +2037,7 @@ static struct virtqueue *vring_create_virtqueue_packed(
if (!vq)
goto err_vq;
 
+   vring_virtqueue_set_ops(>vq);
vq->vq.callback = callback;
vq->vq.vdev = vdev;
vq->vq.name = name;
@@ -2117,14 +2119,14 @@ static int virtqueue_resize_packed(struct virtqueue 
*_vq, u32 num)
  * Generic functions and exported symbols.
  */
 
-static inline int virtqueue_add(struct virtqueue *_vq,
-   struct scatterlist *sgs[],
-   unsigned int total_sg,
-   unsigned int out_sgs,
-   unsigned int in_sgs,
-   void *data,
-   void *ctx,
-   gfp_t gfp)
+int vring_virtqueue_add_sgs(struct virtqueue *_vq,
+   struct scatterlist *sgs[],
+   unsigned int total_sg,
+   unsigned int out_sgs,
+   unsigned int in_sgs,
+   void *data,
+   void *ctx,
+   gfp_t gfp)
 {
struct vring_virtqueue *vq = to_vvq(_vq);
 
@@ -2133,112 +2135,10 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 virtqueue_add_split(_vq, sgs, total_sg,
out_sgs, in_sgs, data, ctx, gfp);
 }
+EXPORT_SYMBOL_GPL(vring_virtqueue_add_sgs);
 
 /**
- * virtqueue_add_sgs - expose buffers to other end
- * @_vq: the struct virtqueue we're talking about.
- * @sgs: array of terminated scatterlists.
- * @out_sgs: the number of scatterlists readable by other side
- * @in_sgs: the number of scatterlists which are writable (after readable ones)
- * @data: the token identifying the buffer.
- * @gfp: how to do memory allocations (if necessary).
- *
- * Caller must ensure we don't call this with other virtqueue operations
- * at the same time (except where noted).
- *
- * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
- */
-int virtqueue_add_sgs(struct virtqueue *_vq,
- struct scatterlist *sgs[],
- unsigned int out_sgs,
- unsigned int in_sgs,
- void *data,
- gfp_t gfp)
-{
-   unsigned int i, total_sg = 0;
-
-   /* Count them first. */
-   for (i = 0; i < out_sgs + in_sgs; i++) {
-   struct scatterlist *sg;
-
-   for (sg = sgs[i]; sg; sg = sg_next(sg))
-   total_sg++;
-   }
-   return virtqueue_add(_vq, sgs, total_sg, out_sgs, in_sgs,
-data, NULL, gfp);
-}
-EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
-
-/**
- * virtqueue_add_outbuf - expose output buffers to other end
- * @vq: the struct virtqueue we're talking about.
- * @sg: scatterlist (must be well-formed and terminated!)
- * @num: the number of entries in @sg readable by other side
- * @data: the token identifying the buffer.
- * @gfp: how to do memory allocations (if necessary).
- *
- * Caller must ensure we don't call this with other virtqueue operations
- * at the same time (except where noted).
- *
- * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
- */
-int virtqueue_add_outbuf(struct virtqueue *vq,
-struct scatterlist *sg, u

[PATCH v2 2/2] tools/virtio: implement virtqueue in test

2023-05-16 Thread zhenwei pi via Virtualization
virtqueue related functions has been abstract since commit
("virtio: abstract virtqueue related methods"), add compatible for
abstract API.

Signed-off-by: zhenwei pi 
---
 tools/virtio/linux/virtio.h | 355 
 1 file changed, 324 insertions(+), 31 deletions(-)

diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index 5d3440f474dd..3531dba53584 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -4,6 +4,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct device {
void *parent;
@@ -27,46 +28,338 @@ struct virtqueue {
unsigned int num_max;
void *priv;
bool reset;
+   bool abstract;
+
+   /* abstract operations */
+   int (*add_sgs)(struct virtqueue *vq, struct scatterlist *sgs[],
+  unsigned int total_sg,
+  unsigned int out_sgs, unsigned int in_sgs,
+  void *data, void *ctx, gfp_t gfp);
+   bool (*kick_prepare)(struct virtqueue *vq);
+   bool (*notify)(struct virtqueue *vq);
+   unsigned int (*enable_cb_prepare)(struct virtqueue *vq);
+   bool (*enable_cb_delayed)(struct virtqueue *vq);
+   void (*disable_cb)(struct virtqueue *vq);
+   bool (*poll)(struct virtqueue *vq, unsigned int idx);
+   void *(*get_buf_ctx)(struct virtqueue *vq, unsigned int *len, void 
**ctx);
+   void *(*detach_unused_buf)(struct virtqueue *vq);
+   unsigned int (*get_vring_size)(const struct virtqueue *vq);
+   int (*resize)(struct virtqueue *vq, u32 num,
+ void (*recycle)(struct virtqueue *vq, void *buf));
+   void (*__break)(struct virtqueue *vq);
+   void (*__unbreak)(struct virtqueue *vq);
+   bool (*is_broken)(const struct virtqueue *vq);
 };
 
-/* Interfaces exported by virtio_ring. */
-int virtqueue_add_sgs(struct virtqueue *vq,
- struct scatterlist *sgs[],
- unsigned int out_sgs,
- unsigned int in_sgs,
- void *data,
- gfp_t gfp);
+/**
+ * virtqueue_add_sgs - expose buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sgs: array of terminated scatterlists.
+ * @out_sgs: the number of scatterlists readable by other side
+ * @in_sgs: the number of scatterlists which are writable (after readable ones)
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+static inline int virtqueue_add_sgs(struct virtqueue *vq,
+   struct scatterlist *sgs[],
+   unsigned int out_sgs,
+   unsigned int in_sgs,
+   void *data, gfp_t gfp)
+{
+   unsigned int i, total_sg = 0;
 
-int virtqueue_add_outbuf(struct virtqueue *vq,
-struct scatterlist sg[], unsigned int num,
-void *data,
-gfp_t gfp);
+   /* Count them first. */
+   for (i = 0; i < out_sgs + in_sgs; i++) {
+   struct scatterlist *sg;
 
-int virtqueue_add_inbuf(struct virtqueue *vq,
-   struct scatterlist sg[], unsigned int num,
-   void *data,
-   gfp_t gfp);
+   for (sg = sgs[i]; sg; sg = sg_next(sg))
+   total_sg++;
+   }
 
-bool virtqueue_kick(struct virtqueue *vq);
+   if (likely(!vq->abstract))
+   return vring_virtqueue_add_sgs(vq, sgs, total_sg, out_sgs, 
in_sgs, data, NULL, gfp);
 
-void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
+   return vq->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs, data, NULL, gfp);
+}
 
-void virtqueue_disable_cb(struct virtqueue *vq);
+/**
+ * virtqueue_add_outbuf - expose output buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg readable by other side
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+static inline int virtqueue_add_outbuf(struct virtqueue *vq,
+  struct scatterlist *sg,
+  unsigned int num,
+  void *data,
+  gfp_t gfp)
+{
+   if (likely(!vq->abstract))
+   return vring_virtqueue_add_sgs(vq, , num, 1, 0, data, NULL, 
gfp);
 
-bool virt

Re: Re: Re: [PATCH 1/2] virtio: abstract virtqueue related methods

2023-05-12 Thread zhenwei pi via Virtualization

On 5/12/23 19:35, Michael S. Tsirkin wrote:

On Fri, May 12, 2023 at 07:09:40PM +0800, zhenwei pi wrote:

On 5/12/23 18:46, Michael S. Tsirkin wrote:

On Fri, May 12, 2023 at 05:46:17PM +0800, zhenwei pi wrote:

There is already a virtqueue abstract structure in virtio subsystem
(see struct virtqueue in include/linux/virtio.h), however the vring
based virtqueue is used only in the past years, the virtqueue related
methods mix much with vring(just look like the codes, virtqueue_xxx
functions are implemented in virtio_ring.c).

Abstract virtqueue related methods(see struct virtqueue_ops), and
separate virtqueue_xxx symbols from vring. This allows a non-vring
based transport in the future. With this change, the following symbols
are exported from virtio.ko instead of virtio_ring.ko:
virtqueue_add_sgs
virtqueue_add_outbuf
virtqueue_add_inbuf
virtqueue_add_inbuf_ctx
virtqueue_kick_prepare
virtqueue_notify
virtqueue_kick
virtqueue_enable_cb_prepare
virtqueue_enable_cb
virtqueue_enable_cb_delayed
virtqueue_disable_cb
virtqueue_poll
virtqueue_get_buf_ctx
virtqueue_get_buf
virtqueue_detach_unused_buf
virtqueue_get_vring_size
virtqueue_resize
virtqueue_is_broken
virtio_break_device
__virtio_unbreak_device

Cc: Stefan Hajnoczi 
Signed-off-by: zhenwei pi 
---
   drivers/virtio/virtio.c  | 362 +++
   drivers/virtio/virtio_ring.c | 282 +--
   include/linux/virtio.h   |  29 +++
   3 files changed, 443 insertions(+), 230 deletions(-)

diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 3893dc29eb26..8d8715a10f26 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -553,6 +553,368 @@ int virtio_device_restore(struct virtio_device *dev)
   EXPORT_SYMBOL_GPL(virtio_device_restore);
   #endif
+/**
+ * virtqueue_add_sgs - expose buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sgs: array of terminated scatterlists.
+ * @out_sgs: the number of scatterlists readable by other side
+ * @in_sgs: the number of scatterlists which are writable (after readable ones)
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+ unsigned int out_sgs, unsigned int in_sgs,
+ void *data, gfp_t gfp)
+{
+   unsigned int i, total_sg = 0;
+
+   /* Count them first. */
+   for (i = 0; i < out_sgs + in_sgs; i++) {
+   struct scatterlist *sg;
+
+   for (sg = sgs[i]; sg; sg = sg_next(sg))
+   total_sg++;
+   }
+   return vq->ops->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
+   data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_sgs);



Hmm this kind of indirection on data path is going to be costly
performance-wise especially when retpolines are in place.

Any data on this?



Hi,

1, What about moving these functions into virtio.h and declare them as
static inline?


This will do nothing to remove indirection.


2, what about moving method fields into struct virtqueue?


This gets rid of one level of indirection but the big problem
is indirect function call due to retpolines, this remains.



Then we have struct like:
struct virtqueue {
struct list_head list;
...
void *priv;

/* virtqueue specific operations */
 int (*add_sgs)(struct virtqueue *vq, struct scatterlist *sgs[],
unsigned int total_sg,
unsigned int out_sgs, unsigned int in_sgs,
void *data, void *ctx, gfp_t gfp);
...
}

and functions like:
static inline int virtqueue_add_sgs(...)
{
 unsigned int i, total_sg = 0;

 /* Count them first. */
 for (i = 0; i < out_sgs + in_sgs; i++) {
 struct scatterlist *sg;

 for (sg = sgs[i]; sg; sg = sg_next(sg))
 total_sg++;
 }
 return vq->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
data, NULL, gfp);
}


Maybe a flag in vq:
bool abstract; /* use ops to add/get bufs and kick */
and then
if (unlikely(vq->abstract))
 return vq->ops->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
 data, NULL, gfp);

transport then just sets the ops if it wants abstract vqs,
and core then skips the vring.



If [1] is acceptable, we can also reduce changes in patch 'tools/virtio:
implement virtqueue in test'.


Yea that one shouldn't be there.


--
zhenwei pi




OK, I'll try and send a next version 

Re: Re: [PATCH 1/2] virtio: abstract virtqueue related methods

2023-05-12 Thread zhenwei pi via Virtualization

On 5/12/23 18:46, Michael S. Tsirkin wrote:

On Fri, May 12, 2023 at 05:46:17PM +0800, zhenwei pi wrote:

There is already a virtqueue abstract structure in virtio subsystem
(see struct virtqueue in include/linux/virtio.h), however the vring
based virtqueue is used only in the past years, the virtqueue related
methods mix much with vring(just look like the codes, virtqueue_xxx
functions are implemented in virtio_ring.c).

Abstract virtqueue related methods(see struct virtqueue_ops), and
separate virtqueue_xxx symbols from vring. This allows a non-vring
based transport in the future. With this change, the following symbols
are exported from virtio.ko instead of virtio_ring.ko:
   virtqueue_add_sgs
   virtqueue_add_outbuf
   virtqueue_add_inbuf
   virtqueue_add_inbuf_ctx
   virtqueue_kick_prepare
   virtqueue_notify
   virtqueue_kick
   virtqueue_enable_cb_prepare
   virtqueue_enable_cb
   virtqueue_enable_cb_delayed
   virtqueue_disable_cb
   virtqueue_poll
   virtqueue_get_buf_ctx
   virtqueue_get_buf
   virtqueue_detach_unused_buf
   virtqueue_get_vring_size
   virtqueue_resize
   virtqueue_is_broken
   virtio_break_device
   __virtio_unbreak_device

Cc: Stefan Hajnoczi 
Signed-off-by: zhenwei pi 
---
  drivers/virtio/virtio.c  | 362 +++
  drivers/virtio/virtio_ring.c | 282 +--
  include/linux/virtio.h   |  29 +++
  3 files changed, 443 insertions(+), 230 deletions(-)

diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 3893dc29eb26..8d8715a10f26 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -553,6 +553,368 @@ int virtio_device_restore(struct virtio_device *dev)
  EXPORT_SYMBOL_GPL(virtio_device_restore);
  #endif
  
+/**

+ * virtqueue_add_sgs - expose buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sgs: array of terminated scatterlists.
+ * @out_sgs: the number of scatterlists readable by other side
+ * @in_sgs: the number of scatterlists which are writable (after readable ones)
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+ unsigned int out_sgs, unsigned int in_sgs,
+ void *data, gfp_t gfp)
+{
+   unsigned int i, total_sg = 0;
+
+   /* Count them first. */
+   for (i = 0; i < out_sgs + in_sgs; i++) {
+   struct scatterlist *sg;
+
+   for (sg = sgs[i]; sg; sg = sg_next(sg))
+   total_sg++;
+   }
+   return vq->ops->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
+   data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_sgs);



Hmm this kind of indirection on data path is going to be costly
performance-wise especially when retpolines are in place.

Any data on this?



Hi,

1, What about moving these functions into virtio.h and declare them as 
static inline?

2, what about moving method fields into struct virtqueue?

Then we have struct like:
struct virtqueue {
struct list_head list;
...
void *priv;

/* virtqueue specific operations */
int (*add_sgs)(struct virtqueue *vq, struct scatterlist *sgs[],
   unsigned int total_sg,
   unsigned int out_sgs, unsigned int in_sgs,
   void *data, void *ctx, gfp_t gfp);
...
}

and functions like:
static inline int virtqueue_add_sgs(...)
{
unsigned int i, total_sg = 0;

/* Count them first. */
for (i = 0; i < out_sgs + in_sgs; i++) {
struct scatterlist *sg;

for (sg = sgs[i]; sg; sg = sg_next(sg))
total_sg++;
}
return vq->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
   data, NULL, gfp);
}

If [1] is acceptable, we can also reduce changes in patch 'tools/virtio: 
implement virtqueue in test'.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH 2/2] tools/virtio: implement virtqueue in test

2023-05-12 Thread zhenwei pi via Virtualization
virtqueue related functions has been removed from virtio_ring.c since
commit("virtio: abstract virtqueue related methods"), rather than
compiling with drivers/virtio/virtio.c, implement virtqueue functions
here.

Signed-off-by: zhenwei pi 
---
 tools/virtio/Makefile   |   4 +-
 tools/virtio/linux/virtio.h |  30 +++
 tools/virtio/virtqueue.c| 367 
 3 files changed, 399 insertions(+), 2 deletions(-)
 create mode 100644 tools/virtio/virtqueue.c

diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile
index 7b7139d97d74..a98d409aae7c 100644
--- a/tools/virtio/Makefile
+++ b/tools/virtio/Makefile
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 all: test mod
 test: virtio_test vringh_test
-virtio_test: virtio_ring.o virtio_test.o
-vringh_test: vringh_test.o vringh.o virtio_ring.o
+virtio_test: virtio_ring.o virtio_test.o virtqueue.o
+vringh_test: vringh_test.o vringh.o virtio_ring.o virtqueue.o
 
 CFLAGS += -g -O2 -Werror -Wno-maybe-uninitialized -Wall -I. -I../include/ -I 
../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing 
-fno-common -MMD -U_FORTIFY_SOURCE -include ../../include/linux/kconfig.h 
-mfunction-return=thunk -fcf-protection=none -mindirect-branch-register
 CFLAGS += -pthread
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index 5d3440f474dd..cb27a1105552 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -17,6 +17,35 @@ struct virtio_device {
const struct virtio_config_ops *config;
 };
 
+struct virtqueue;
+
+/**
+ * struct virtqueue_ops - abstract operations for a virtqueue.
+ *
+ * Descriptions of each field see the comments in virtio.c
+ */
+struct virtqueue_ops {
+   int (*add_sgs)(struct virtqueue *vq, struct scatterlist *sgs[],
+  unsigned int total_sg,
+  unsigned int out_sgs, unsigned int in_sgs,
+  void *data, void *ctx, gfp_t gfp);
+   bool (*kick_prepare)(struct virtqueue *vq);
+   bool (*notify)(struct virtqueue *vq);
+   unsigned int (*enable_cb_prepare)(struct virtqueue *vq);
+   bool (*enable_cb)(struct virtqueue *vq);
+   bool (*enable_cb_delayed)(struct virtqueue *vq);
+   void (*disable_cb)(struct virtqueue *vq);
+   bool (*poll)(struct virtqueue *vq, unsigned int idx);
+   void *(*get_buf_ctx)(struct virtqueue *vq, unsigned int *len, void 
**ctx);
+   void *(*detach_unused_buf)(struct virtqueue *vq);
+   unsigned int (*get_vring_size)(const struct virtqueue *vq);
+   int (*resize)(struct virtqueue *vq, u32 num,
+ void (*recycle)(struct virtqueue *vq, void *buf));
+   void (*__break)(struct virtqueue *vq);
+   void (*__unbreak)(struct virtqueue *vq);
+   bool (*is_broken)(const struct virtqueue *vq);
+};
+
 struct virtqueue {
struct list_head list;
void (*callback)(struct virtqueue *vq);
@@ -27,6 +56,7 @@ struct virtqueue {
unsigned int num_max;
void *priv;
bool reset;
+   struct virtqueue_ops *ops;
 };
 
 /* Interfaces exported by virtio_ring. */
diff --git a/tools/virtio/virtqueue.c b/tools/virtio/virtqueue.c
new file mode 100644
index ..1f86a414f628
--- /dev/null
+++ b/tools/virtio/virtqueue.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+#include 
+#include 
+#include 
+
+/**
+ * virtqueue_add_sgs - expose buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sgs: array of terminated scatterlists.
+ * @out_sgs: the number of scatterlists readable by other side
+ * @in_sgs: the number of scatterlists which are writable (after readable ones)
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+ unsigned int out_sgs, unsigned int in_sgs,
+ void *data, gfp_t gfp)
+{
+   unsigned int i, total_sg = 0;
+
+   /* Count them first. */
+   for (i = 0; i < out_sgs + in_sgs; i++) {
+   struct scatterlist *sg;
+
+   for (sg = sgs[i]; sg; sg = sg_next(sg))
+   total_sg++;
+   }
+   return vq->ops->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
+   data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
+
+/**
+ * virtqueue_add_outbuf - expose output buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg readable by other side
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't c

[PATCH 1/2] virtio: abstract virtqueue related methods

2023-05-12 Thread zhenwei pi via Virtualization
There is already a virtqueue abstract structure in virtio subsystem
(see struct virtqueue in include/linux/virtio.h), however the vring
based virtqueue is used only in the past years, the virtqueue related
methods mix much with vring(just look like the codes, virtqueue_xxx
functions are implemented in virtio_ring.c).

Abstract virtqueue related methods(see struct virtqueue_ops), and
separate virtqueue_xxx symbols from vring. This allows a non-vring
based transport in the future. With this change, the following symbols
are exported from virtio.ko instead of virtio_ring.ko:
  virtqueue_add_sgs
  virtqueue_add_outbuf
  virtqueue_add_inbuf
  virtqueue_add_inbuf_ctx
  virtqueue_kick_prepare
  virtqueue_notify
  virtqueue_kick
  virtqueue_enable_cb_prepare
  virtqueue_enable_cb
  virtqueue_enable_cb_delayed
  virtqueue_disable_cb
  virtqueue_poll
  virtqueue_get_buf_ctx
  virtqueue_get_buf
  virtqueue_detach_unused_buf
  virtqueue_get_vring_size
  virtqueue_resize
  virtqueue_is_broken
  virtio_break_device
  __virtio_unbreak_device

Cc: Stefan Hajnoczi 
Signed-off-by: zhenwei pi 
---
 drivers/virtio/virtio.c  | 362 +++
 drivers/virtio/virtio_ring.c | 282 +--
 include/linux/virtio.h   |  29 +++
 3 files changed, 443 insertions(+), 230 deletions(-)

diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 3893dc29eb26..8d8715a10f26 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -553,6 +553,368 @@ int virtio_device_restore(struct virtio_device *dev)
 EXPORT_SYMBOL_GPL(virtio_device_restore);
 #endif
 
+/**
+ * virtqueue_add_sgs - expose buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sgs: array of terminated scatterlists.
+ * @out_sgs: the number of scatterlists readable by other side
+ * @in_sgs: the number of scatterlists which are writable (after readable ones)
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+ unsigned int out_sgs, unsigned int in_sgs,
+ void *data, gfp_t gfp)
+{
+   unsigned int i, total_sg = 0;
+
+   /* Count them first. */
+   for (i = 0; i < out_sgs + in_sgs; i++) {
+   struct scatterlist *sg;
+
+   for (sg = sgs[i]; sg; sg = sg_next(sg))
+   total_sg++;
+   }
+   return vq->ops->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs,
+   data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
+
+/**
+ * virtqueue_add_outbuf - expose output buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg readable by other side
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_outbuf(struct virtqueue *vq, struct scatterlist *sg,
+unsigned int num, void *data, gfp_t gfp)
+{
+   return vq->ops->add_sgs(vq, , num, 1, 0, data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
+
+/**
+ * virtqueue_add_inbuf - expose input buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg writable by other side
+ * @data: the token identifying the buffer.
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int virtqueue_add_inbuf(struct virtqueue *vq, struct scatterlist *sg,
+   unsigned int num, void *data, gfp_t gfp)
+{
+   return vq->ops->add_sgs(vq, , num, 0, 1, data, NULL, gfp);
+}
+EXPORT_SYMBOL_GPL(virtqueue_add_inbuf);
+
+/**
+ * virtqueue_add_inbuf_ctx - expose input buffers to other end
+ * @vq: the struct virtqueue we're talking about.
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg writable by other side
+ * @data: the token identifying the buffer.
+ * @ctx: extra context for the token
+ * @gfp: how to do memory allocations (if necessary).
+ *
+ * Caller must ensure we don't call this with other virtqueue operations
+ * at the same time (except where noted).
+ *
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
+ */
+int vi

[PATCH 0/2] virtio: abstract virtqueue related methods

2023-05-12 Thread zhenwei pi via Virtualization
Hi,

3 weeks ago, I posted a proposal 'Virtio Over Fabrics':
https://lists.oasis-open.org/archives/virtio-comment/202304/msg00442.html

Jason and Stefan pointed out that a non-vring based virtqueue has a
chance to overwrite virtqueue instead of using vring virtqueue.

Then I try to abstract virtqueue related methods in this series, the
details changes see the comment of patch 'virtio: abstract virtqueue related 
methods'.

Something is still remained:
- __virtqueue_break/__virtqueue_unbreak is supposed to use by internal
  virtio core, I'd like to rename them to vring_virtqueue_break
  /vring_virtqueue_unbreak. Is this reasonable?
- virtqueue_get_desc_addr/virtqueue_get_avail_addr/virtqueue_get_used_addr
  /virtqueue_get_vring is vring specific, I'd like to rename them like
  vring_virtqueue_get_desc_addr. Is this reasonable?
- there are still some functions in virtio_ring.c with prefix *virtqueue*,
  for example 'virtqueue_add_split', just keep it or rename it to
  'vring_virtqueue_add_split'?

zhenwei pi (2):
  virtio: abstract virtqueue related methods
  tools/virtio: implement virtqueue in test

 drivers/virtio/virtio.c  | 362 ++
 drivers/virtio/virtio_ring.c | 282 +--
 include/linux/virtio.h   |  29 +++
 tools/virtio/Makefile|   4 +-
 tools/virtio/linux/virtio.h  |  30 +++
 tools/virtio/virtqueue.c | 367 +++
 6 files changed, 842 insertions(+), 232 deletions(-)
 create mode 100644 tools/virtio/virtqueue.c

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH] virtio_ring: use u32 for virtio_max_dma_size

2023-05-10 Thread zhenwei pi via Virtualization




On 5/10/23 11:26, Xuan Zhuo wrote:

On Wed, 10 May 2023 10:54:37 +0800, zhenwei pi  wrote:

Both split ring and packed ring use 32bits to describe the length of
a descriptor: see struct vring_desc and struct vring_packed_desc.
This means the max segment size supported by virtio is U32_MAX.

An example of virtio_max_dma_size in virtio_blk.c:
   u32 v, max_size;

   max_size = virtio_max_dma_size(vdev);  -> implicit convert
   err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
  struct virtio_blk_config, size_max, );
   max_size = min(max_size, v);

There is a risk during implicit convert here, once virtio_max_dma_size
returns 4G, max_size becomes 0.

Fixes: e6d6dd6c875e ("virtio: Introduce virtio_max_dma_size()")
Cc: Joerg Roedel 
Signed-off-by: zhenwei pi 
---
  drivers/virtio/virtio_ring.c | 12 
  include/linux/virtio.h   |  2 +-
  2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..55cfecf030a1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -289,12 +289,16 @@ static bool vring_use_dma_api(const struct virtio_device 
*vdev)
return false;
  }

-size_t virtio_max_dma_size(const struct virtio_device *vdev)
+u32 virtio_max_dma_size(const struct virtio_device *vdev)



LGTM

But, should we change the parameter to vq, then use the dma_dev?

@Jason

Thanks.



The max DMA size is a attribute of a virtio device rather than any VQ, 
so I guess virtio_max_dma_size(const struct virtio_device *vdev) is clear.


On the other hand, if changing the parameter to vq, we need select a VQ, 
then the question is:

1, which VQ to select? VQ0 or a random one? this leads confusing.
2, The virtio spec defines: Each device can have zero or more virtqueues





  {
-   size_t max_segment_size = SIZE_MAX;
+   u32 max_segment_size = U32_MAX;

-   if (vring_use_dma_api(vdev))
-   max_segment_size = dma_max_mapping_size(vdev->dev.parent);
+   if (vring_use_dma_api(vdev)) {
+   size_t max_dma_size = dma_max_mapping_size(vdev->dev.parent);
+
+   if (max_dma_size < max_segment_size)
+   max_segment_size = max_dma_size;
+   }

return max_segment_size;
  }
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index b93238db94e3..1a605f408329 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -147,7 +147,7 @@ int virtio_device_restore(struct virtio_device *dev);
  #endif
  void virtio_reset_device(struct virtio_device *dev);

-size_t virtio_max_dma_size(const struct virtio_device *vdev);
+u32 virtio_max_dma_size(const struct virtio_device *vdev);

  #define virtio_device_for_each_vq(vdev, vq) \
list_for_each_entry(vq, >vqs, list)
--
2.20.1



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH] virtio_ring: use u32 for virtio_max_dma_size

2023-05-09 Thread zhenwei pi via Virtualization




On 5/10/23 11:39, Michael S. Tsirkin wrote:

On Wed, May 10, 2023 at 10:54:37AM +0800, zhenwei pi wrote:

Both split ring and packed ring use 32bits to describe the length of
a descriptor: see struct vring_desc and struct vring_packed_desc.
This means the max segment size supported by virtio is U32_MAX.

An example of virtio_max_dma_size in virtio_blk.c:
   u32 v, max_size;

   max_size = virtio_max_dma_size(vdev);  -> implicit convert
   err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
  struct virtio_blk_config, size_max, );
   max_size = min(max_size, v);

There is a risk during implicit convert here, once virtio_max_dma_size
returns 4G, max_size becomes 0.

Fixes: e6d6dd6c875e ("virtio: Introduce virtio_max_dma_size()")
Cc: Joerg Roedel 
Signed-off-by: zhenwei pi 



is this a theoretical concern or do you manage to trigger this
somehow?



I never hit any issue about this, I notice here during diving into the 
symbols exported by virtio_ring.ko.



---
  drivers/virtio/virtio_ring.c | 12 
  include/linux/virtio.h   |  2 +-
  2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..55cfecf030a1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -289,12 +289,16 @@ static bool vring_use_dma_api(const struct virtio_device 
*vdev)
return false;
  }
  
-size_t virtio_max_dma_size(const struct virtio_device *vdev)

+u32 virtio_max_dma_size(const struct virtio_device *vdev)
  {
-   size_t max_segment_size = SIZE_MAX;
+   u32 max_segment_size = U32_MAX;
  
-	if (vring_use_dma_api(vdev))

-   max_segment_size = dma_max_mapping_size(vdev->dev.parent);
+   if (vring_use_dma_api(vdev)) {
+   size_t max_dma_size = dma_max_mapping_size(vdev->dev.parent);
+
+   if (max_dma_size < max_segment_size)
+   max_segment_size = max_dma_size;
+   }
  
  	return max_segment_size;

  }
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index b93238db94e3..1a605f408329 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -147,7 +147,7 @@ int virtio_device_restore(struct virtio_device *dev);
  #endif
  void virtio_reset_device(struct virtio_device *dev);
  
-size_t virtio_max_dma_size(const struct virtio_device *vdev);

+u32 virtio_max_dma_size(const struct virtio_device *vdev);
  
  #define virtio_device_for_each_vq(vdev, vq) \

list_for_each_entry(vq, >vqs, list)
--
2.20.1




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH] virtio_ring: use u32 for virtio_max_dma_size

2023-05-09 Thread zhenwei pi via Virtualization
Both split ring and packed ring use 32bits to describe the length of
a descriptor: see struct vring_desc and struct vring_packed_desc.
This means the max segment size supported by virtio is U32_MAX.

An example of virtio_max_dma_size in virtio_blk.c:
  u32 v, max_size;

  max_size = virtio_max_dma_size(vdev);  -> implicit convert
  err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
 struct virtio_blk_config, size_max, );
  max_size = min(max_size, v);

There is a risk during implicit convert here, once virtio_max_dma_size
returns 4G, max_size becomes 0.

Fixes: e6d6dd6c875e ("virtio: Introduce virtio_max_dma_size()")
Cc: Joerg Roedel 
Signed-off-by: zhenwei pi 
---
 drivers/virtio/virtio_ring.c | 12 
 include/linux/virtio.h   |  2 +-
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index c5310eaf8b46..55cfecf030a1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -289,12 +289,16 @@ static bool vring_use_dma_api(const struct virtio_device 
*vdev)
return false;
 }
 
-size_t virtio_max_dma_size(const struct virtio_device *vdev)
+u32 virtio_max_dma_size(const struct virtio_device *vdev)
 {
-   size_t max_segment_size = SIZE_MAX;
+   u32 max_segment_size = U32_MAX;
 
-   if (vring_use_dma_api(vdev))
-   max_segment_size = dma_max_mapping_size(vdev->dev.parent);
+   if (vring_use_dma_api(vdev)) {
+   size_t max_dma_size = dma_max_mapping_size(vdev->dev.parent);
+
+   if (max_dma_size < max_segment_size)
+   max_segment_size = max_dma_size;
+   }
 
return max_segment_size;
 }
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index b93238db94e3..1a605f408329 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -147,7 +147,7 @@ int virtio_device_restore(struct virtio_device *dev);
 #endif
 void virtio_reset_device(struct virtio_device *dev);
 
-size_t virtio_max_dma_size(const struct virtio_device *vdev);
+u32 virtio_max_dma_size(const struct virtio_device *vdev);
 
 #define virtio_device_for_each_vq(vdev, vq) \
list_for_each_entry(vq, >vqs, list)
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: [PATCH] virtio-crypto: fix memory leak in virtio_crypto_alg_skcipher_close_session()

2022-11-14 Thread zhenwei pi

Looks good to me, thanks!

Acked-by: zhenwei pi

On 11/14/22 19:07, Wei Yongjun wrote:

From: Wei Yongjun 

'vc_ctrl_req' is alloced in virtio_crypto_alg_skcipher_close_session(),
and should be freed in the invalid ctrl_status->status error handling
case. Otherwise there is a memory leak.

Fixes: 0756ad15b1fe ("virtio-crypto: use private buffer for control request")
Signed-off-by: Wei Yongjun 
---
  drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
index e553ccadbcbc..e5876286828b 100644
--- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
@@ -239,7 +239,8 @@ static int virtio_crypto_alg_skcipher_close_session(
pr_err("virtio_crypto: Close session failed status: %u, session_id: 
0x%llx\n",
ctrl_status->status, destroy_session->session_id);
  
-		return -EINVAL;

+   err = -EINVAL;
+   goto out;
    }
  
  	err = 0;


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v10 1/1] crypto: Introduce RSA algorithm

2022-06-11 Thread zhenwei pi
There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Reviewed-by: Gonglei 
Signed-off-by: lei he 
---
 backends/cryptodev-builtin.c  | 276 +
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c  |  32 ++-
 hw/virtio/virtio-crypto.c | 323 --
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h|  83 ++--
 6 files changed, 609 insertions(+), 144 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 0671bf9f3e..125cbad1d3 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "standard-headers/linux/virtio_crypto.h"
 #include "crypto/cipher.h"
+#include "crypto/akcipher.h"
 #include "qom/object.h"
 
 
@@ -42,10 +43,11 @@ typedef struct CryptoDevBackendBuiltinSession {
 QCryptoCipher *cipher;
 uint8_t direction; /* encryption or decryption */
 uint8_t type; /* cipher? hash? aead? */
+QCryptoAkCipher *akcipher;
 QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
 } CryptoDevBackendBuiltinSession;
 
-/* Max number of symmetric sessions */
+/* Max number of symmetric/asymmetric sessions */
 #define MAX_NUM_SESSIONS 256
 
 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN512
@@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
 backend->conf.crypto_services =
  1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
 backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
 backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
 /*
  * Set the Maximum length of crypto request.
  * Why this value? Just avoid to overflow when
  * memory allocation for each crypto request.
  */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
 backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
 backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,55 @@ err:
return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return 

[PATCH v10 0/1] Introduce akcipher service for virtio-crypto

2022-06-11 Thread zhenwei pi
 Introduce akcipher types to qapi
- Add test/benchmark suite for akcipher class
- Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
 - crypto: Introduce akcipher crypto class
 - virtio-crypto: Introduce RSA algorithm

v1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Zhenwei Pi (1):
  crypto: Introduce RSA algorithm

 backends/cryptodev-builtin.c  | 276 +
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c  |  32 ++-
 hw/virtio/virtio-crypto.c | 323 --
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h|  83 ++--
 6 files changed, 609 insertions(+), 144 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

[PATCH v9 1/1] crypto: Introduce RSA algorithm

2022-06-10 Thread zhenwei pi
There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Reviewed-by: Gonglei 
Signed-off-by: lei he 
---
 backends/cryptodev-builtin.c  | 275 +
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c  |  32 ++-
 hw/virtio/virtio-crypto.c | 323 --
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h|  83 ++--
 6 files changed, 608 insertions(+), 144 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 0671bf9f3e..ed73ea789b 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "standard-headers/linux/virtio_crypto.h"
 #include "crypto/cipher.h"
+#include "crypto/akcipher.h"
 #include "qom/object.h"
 
 
@@ -42,10 +43,11 @@ typedef struct CryptoDevBackendBuiltinSession {
 QCryptoCipher *cipher;
 uint8_t direction; /* encryption or decryption */
 uint8_t type; /* cipher? hash? aead? */
+QCryptoAkCipher *akcipher;
 QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
 } CryptoDevBackendBuiltinSession;
 
-/* Max number of symmetric sessions */
+/* Max number of symmetric/asymmetric sessions */
 #define MAX_NUM_SESSIONS 256
 
 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN512
@@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
 backend->conf.crypto_services =
  1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
 backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
 backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
 /*
  * Set the Maximum length of crypto request.
  * Why this value? Just avoid to overflow when
  * memory allocation for each crypto request.
  */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
 backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
 backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,54 @@ err:
return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return 

[PATCH v9 0/1] Introduce akcipher service for virtio-crypto

2022-06-10 Thread zhenwei pi
 suite for akcipher class
- Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
 - crypto: Introduce akcipher crypto class
 - virtio-crypto: Introduce RSA algorithm

v1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Zhenwei Pi (1):
  crypto: Introduce RSA algorithm

 backends/cryptodev-builtin.c  | 275 +
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c  |  32 ++-
 hw/virtio/virtio-crypto.c | 323 --
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h|  83 ++--
 6 files changed, 608 insertions(+), 144 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

PING: RE: RE: [PATCH v8 1/1] crypto: Introduce RSA algorithm

2022-06-08 Thread zhenwei pi

Hi, Michael

QEMU side was reviewed by Gonglei a week ago. To avoid this to be 
ignored, PING!


On 5/31/22 20:08, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Tuesday, May 31, 2022 9:48 AM
To: Gonglei (Arei) 
Cc: qemu-de...@nongnu.org; m...@redhat.com;
virtualization@lists.linux-foundation.org; helei.si...@bytedance.com;
berra...@redhat.com
Subject: Re: RE: [PATCH v8 1/1] crypto: Introduce RSA algorithm

On 5/30/22 21:31, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Friday, May 27, 2022 4:48 PM
To: m...@redhat.com; Gonglei (Arei) 
Cc: qemu-de...@nongnu.org; virtualization@lists.linux-foundation.org;
helei.si...@bytedance.com; berra...@redhat.com; zhenwei pi

Subject: [PATCH v8 1/1] crypto: Introduce RSA algorithm



Skip...


+static int64_t
+virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
+   struct virtio_crypto_akcipher_create_session_req
*sess_req,
+   uint32_t queue_id, uint32_t opcode,
+   struct iovec *iov, unsigned int out_num) {
+VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+CryptoDevBackendSessionInfo info = {0};
+CryptoDevBackendAsymSessionInfo *asym_info;
+int64_t session_id;
+int queue_index;
+uint32_t algo, keytype, keylen;
+g_autofree uint8_t *key = NULL;
+Error *local_err = NULL;
+
+algo = ldl_le_p(_req->para.algo);
+keytype = ldl_le_p(_req->para.keytype);
+keylen = ldl_le_p(_req->para.keylen);
+
+if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
+ && (keytype !=

VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {

+error_report("unsupported asym keytype: %d", keytype);
+return -VIRTIO_CRYPTO_NOTSUPP;
+}
+
+if (keylen) {
+key = g_malloc(keylen);
+if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+virtio_error(vdev, "virtio-crypto asym key incorrect");
+return -EFAULT;


Memory leak.


+}
+iov_discard_front(, _num, keylen);
+}
+
+info.op_code = opcode;
+asym_info = _sess_info;
+asym_info->algo = algo;
+asym_info->keytype = keytype;
+asym_info->keylen = keylen;
+asym_info->key = key;
+switch (asym_info->algo) {
+case VIRTIO_CRYPTO_AKCIPHER_RSA:
+asym_info->u.rsa.padding_algo =
+ldl_le_p(_req->para.u.rsa.padding_algo);
+asym_info->u.rsa.hash_algo =
+ldl_le_p(_req->para.u.rsa.hash_algo);
+break;
+
+/* TODO DSA handling */
+
+default:
+return -VIRTIO_CRYPTO_ERR;
+}
+
+queue_index = virtio_crypto_vq2q(queue_id);
+session_id =
+ cryptodev_backend_create_session(vcrypto->cryptodev,
,
+ queue_index, _err);
+if (session_id < 0) {
+if (local_err) {
+error_report_err(local_err);
+}
+return -VIRTIO_CRYPTO_ERR;
+}
+
+return session_id;


Where to free the key at both normal and exceptional paths?



Hi, Lei

The key is declared with g_autofree:
g_autofree uint8_t *key = NULL;



OK. For the patch:

Reviewed-by: Gonglei 


Regards,
-Gonglei
 



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-06-02 Thread zhenwei pi

On 6/1/22 15:59, David Hildenbrand wrote:

On 01.06.22 04:17, zhenwei pi wrote:

On 5/31/22 12:08, Jue Wang wrote:

On Mon, May 30, 2022 at 8:49 AM Peter Xu  wrote:


On Mon, May 30, 2022 at 07:33:35PM +0800, zhenwei pi wrote:

A VM uses RAM of 2M huge page. Once a MCE(@HVAy in [HVAx,HVAz)) occurs, the
2M([HVAx,HVAz)) of hypervisor becomes unaccessible, but the guest poisons 4K
(@GPAy in [GPAx, GPAz)) only, it may hit another 511 MCE ([GPAx, GPAz)
except GPAy). This is the worse case, so I want to add
   '__le32 corrupted_pages' in struct virtio_balloon_config, it is used in the
next step: reporting 512 * 4K 'corrupted_pages' to the guest, the guest has
a chance to isolate the other 511 pages ahead of time. And the guest
actually loses 2M, fixing 512*4K seems to help significantly.


It sounds hackish to teach a virtio device to assume one page will always
be poisoned in huge page granule.  That's only a limitation to host kernel
not virtio itself.

E.g. there're upstream effort ongoing with enabling doublemap on hugetlbfs
pages so hugetlb pages can be mapped in 4k with it.  It provides potential
possibility to do page poisoning with huge pages in 4k too.  When that'll
be ready the assumption can go away, and that does sound like a better
approach towards this problem.


+1.

A hypervisor should always strive to minimize the guest memory loss.

The HugeTLB double mapping enlightened memory poisoning behavior (only
poison 4K out of a 2MB huge page and 4K in guest) is a much better
solution here. To be completely transparent, it's not _strictly_
required to poison the page (whatever the granularity it is) on the
host side, as long as the following are true:

1. A hypervisor can emulate the _minimized_ (e.g., 4K) the poison to the guest.
2. The host page with the UC error is "isolated" (could be PG_HWPOISON
or in some other way) and prevented from being reused by other
processes.

For #2, PG_HWPOISON and HugeTLB double mapping enlightened memory
poisoning is a good solution.







I assume when talking about "the performance memory drops a lot", you
imply that this patch set can mitigate that performance drop?

But why do you see a performance drop? Because we might lose some
possible THP candidates (in the host or the guest) and you want to plug
does holes? I assume you'll see a performance drop simply because
poisoning memory is expensive, including migrating pages around on CE.

If you have some numbers to share, especially before/after this change,
that would be great.



The CE storm leads 2 problems I have even seen:
1, the memory bandwidth slows down to 10%~20%, and the cycles per
instruction of CPU increases a lot.
2, the THR (/proc/interrupts) interrupts frequently, the CPU has to use a
lot time to handle IRQ.


Totally no good knowledge on CMCI, but if 2) is true then I'm wondering
whether it's necessary to handle the interrupts that frequently.  When I
was reading the Intel CMCI vector handler I stumbled over this comment:

/*
   * The interrupt handler. This is called on every event.
   * Just call the poller directly to log any events.
   * This could in theory increase the threshold under high load,
   * but doesn't for now.
   */
static void intel_threshold_interrupt(void)

I think that matches with what I was thinking..  I mean for 2) not sure
whether it can be seen as a CMCI problem and potentially can be optimized
by adjust the cmci threshold dynamically.


The CE storm caused performance drop is caused by the extra cycles
spent by the ECC steps in memory controller, not in CMCI handling.
This is observed in the Google fleet as well. A good solution is to
monitor the CE rate closely in user space via /dev/mcelog and migrate
all VMs to another host once the CE rate exceeds some threshold.

CMCI is a _background_ interrupt that is not handled in the process
execution context and its handler is setup to switch to poll (1 / 5
min) mode if there are more than ~ a dozen CEs reported via CMCI per
second.


--
Peter Xu



Hi, Andrew, David, Naoya

According to the suggestions, I'd give up the improvement of memory
failure on huge page in this series.

Is it worth recovering corrupted pages for the guest kernel? I'd follow
your decision.


Well, as I said, I am not sure if we really need/want this for a handful
of 4k poisoned pages in a VM. As I suspected, doing so might primarily
be interesting for some sort of de-fragmentation (allow again a higher
order page to be placed at the affected PFNs), not because of the slight
reduction of available memory. A simple VM reboot would get the job
similarly done.



Sure, Let's drop this idea. Thanks to all for the suggestions.

Hi, Naoya
It seems that memory failure notifier is not required currently, so I'll 
not push the next version of:

[PATCH 1/3] memory-failure: Introduce memory failure notifier
[PATCH 2/3] mm/memory-failure.c: support reset PTE during unpoison

Thanks you for review work!


As the poisoning refcount code is alre

Re: Re: Re: [PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-05-31 Thread zhenwei pi

On 5/31/22 12:08, Jue Wang wrote:

On Mon, May 30, 2022 at 8:49 AM Peter Xu  wrote:


On Mon, May 30, 2022 at 07:33:35PM +0800, zhenwei pi wrote:

A VM uses RAM of 2M huge page. Once a MCE(@HVAy in [HVAx,HVAz)) occurs, the
2M([HVAx,HVAz)) of hypervisor becomes unaccessible, but the guest poisons 4K
(@GPAy in [GPAx, GPAz)) only, it may hit another 511 MCE ([GPAx, GPAz)
except GPAy). This is the worse case, so I want to add
  '__le32 corrupted_pages' in struct virtio_balloon_config, it is used in the
next step: reporting 512 * 4K 'corrupted_pages' to the guest, the guest has
a chance to isolate the other 511 pages ahead of time. And the guest
actually loses 2M, fixing 512*4K seems to help significantly.


It sounds hackish to teach a virtio device to assume one page will always
be poisoned in huge page granule.  That's only a limitation to host kernel
not virtio itself.

E.g. there're upstream effort ongoing with enabling doublemap on hugetlbfs
pages so hugetlb pages can be mapped in 4k with it.  It provides potential
possibility to do page poisoning with huge pages in 4k too.  When that'll
be ready the assumption can go away, and that does sound like a better
approach towards this problem.


+1.

A hypervisor should always strive to minimize the guest memory loss.

The HugeTLB double mapping enlightened memory poisoning behavior (only
poison 4K out of a 2MB huge page and 4K in guest) is a much better
solution here. To be completely transparent, it's not _strictly_
required to poison the page (whatever the granularity it is) on the
host side, as long as the following are true:

1. A hypervisor can emulate the _minimized_ (e.g., 4K) the poison to the guest.
2. The host page with the UC error is "isolated" (could be PG_HWPOISON
or in some other way) and prevented from being reused by other
processes.

For #2, PG_HWPOISON and HugeTLB double mapping enlightened memory
poisoning is a good solution.







I assume when talking about "the performance memory drops a lot", you
imply that this patch set can mitigate that performance drop?

But why do you see a performance drop? Because we might lose some
possible THP candidates (in the host or the guest) and you want to plug
does holes? I assume you'll see a performance drop simply because
poisoning memory is expensive, including migrating pages around on CE.

If you have some numbers to share, especially before/after this change,
that would be great.



The CE storm leads 2 problems I have even seen:
1, the memory bandwidth slows down to 10%~20%, and the cycles per
instruction of CPU increases a lot.
2, the THR (/proc/interrupts) interrupts frequently, the CPU has to use a
lot time to handle IRQ.


Totally no good knowledge on CMCI, but if 2) is true then I'm wondering
whether it's necessary to handle the interrupts that frequently.  When I
was reading the Intel CMCI vector handler I stumbled over this comment:

/*
  * The interrupt handler. This is called on every event.
  * Just call the poller directly to log any events.
  * This could in theory increase the threshold under high load,
  * but doesn't for now.
  */
static void intel_threshold_interrupt(void)

I think that matches with what I was thinking..  I mean for 2) not sure
whether it can be seen as a CMCI problem and potentially can be optimized
by adjust the cmci threshold dynamically.


The CE storm caused performance drop is caused by the extra cycles
spent by the ECC steps in memory controller, not in CMCI handling.
This is observed in the Google fleet as well. A good solution is to
monitor the CE rate closely in user space via /dev/mcelog and migrate
all VMs to another host once the CE rate exceeds some threshold.

CMCI is a _background_ interrupt that is not handled in the process
execution context and its handler is setup to switch to poll (1 / 5
min) mode if there are more than ~ a dozen CEs reported via CMCI per
second.


--
Peter Xu



Hi, Andrew, David, Naoya

According to the suggestions, I'd give up the improvement of memory 
failure on huge page in this series.


Is it worth recovering corrupted pages for the guest kernel? I'd follow 
your decision.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: RE: RE: [PATCH v8 1/1] crypto: Introduce RSA algorithm

2022-05-31 Thread zhenwei pi

On 5/31/22 20:08, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Tuesday, May 31, 2022 9:48 AM
To: Gonglei (Arei) 
Cc: qemu-de...@nongnu.org; m...@redhat.com;
virtualization@lists.linux-foundation.org; helei.si...@bytedance.com;
berra...@redhat.com
Subject: Re: RE: [PATCH v8 1/1] crypto: Introduce RSA algorithm

On 5/30/22 21:31, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Friday, May 27, 2022 4:48 PM
To: m...@redhat.com; Gonglei (Arei) 
Cc: qemu-de...@nongnu.org; virtualization@lists.linux-foundation.org;
helei.si...@bytedance.com; berra...@redhat.com; zhenwei pi

Subject: [PATCH v8 1/1] crypto: Introduce RSA algorithm



Skip...





OK. For the patch:

Reviewed-by: Gonglei 


Regards,
-Gonglei
 



Hi, Michael & Lei,

The other patches of this series has been already merged into QEMU, this 
patch is the last one. With this patch, we can test virtio-crypto 
akcipher end-to-end.


Thanks a lot!

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: RE: [PATCH v8 1/1] crypto: Introduce RSA algorithm

2022-05-30 Thread zhenwei pi

On 5/30/22 21:31, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Friday, May 27, 2022 4:48 PM
To: m...@redhat.com; Gonglei (Arei) 
Cc: qemu-de...@nongnu.org; virtualization@lists.linux-foundation.org;
helei.si...@bytedance.com; berra...@redhat.com; zhenwei pi

Subject: [PATCH v8 1/1] crypto: Introduce RSA algorithm



Skip...


+static int64_t
+virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
+   struct virtio_crypto_akcipher_create_session_req
*sess_req,
+   uint32_t queue_id, uint32_t opcode,
+   struct iovec *iov, unsigned int out_num) {
+VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+CryptoDevBackendSessionInfo info = {0};
+CryptoDevBackendAsymSessionInfo *asym_info;
+int64_t session_id;
+int queue_index;
+uint32_t algo, keytype, keylen;
+g_autofree uint8_t *key = NULL;
+Error *local_err = NULL;
+
+algo = ldl_le_p(_req->para.algo);
+keytype = ldl_le_p(_req->para.keytype);
+keylen = ldl_le_p(_req->para.keylen);
+
+if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
+ && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {
+error_report("unsupported asym keytype: %d", keytype);
+return -VIRTIO_CRYPTO_NOTSUPP;
+}
+
+if (keylen) {
+key = g_malloc(keylen);
+if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+virtio_error(vdev, "virtio-crypto asym key incorrect");
+return -EFAULT;


Memory leak.


+}
+iov_discard_front(, _num, keylen);
+}
+
+info.op_code = opcode;
+asym_info = _sess_info;
+asym_info->algo = algo;
+asym_info->keytype = keytype;
+asym_info->keylen = keylen;
+asym_info->key = key;
+switch (asym_info->algo) {
+case VIRTIO_CRYPTO_AKCIPHER_RSA:
+asym_info->u.rsa.padding_algo =
+ldl_le_p(_req->para.u.rsa.padding_algo);
+asym_info->u.rsa.hash_algo =
+ldl_le_p(_req->para.u.rsa.hash_algo);
+break;
+
+/* TODO DSA handling */
+
+default:
+return -VIRTIO_CRYPTO_ERR;
+}
+
+queue_index = virtio_crypto_vq2q(queue_id);
+session_id = cryptodev_backend_create_session(vcrypto->cryptodev,
,
+ queue_index, _err);
+if (session_id < 0) {
+if (local_err) {
+error_report_err(local_err);
+}
+return -VIRTIO_CRYPTO_ERR;
+}
+
+return session_id;


Where to free the key at both normal and exceptional paths?



Hi, Lei

The key is declared with g_autofree:
g_autofree uint8_t *key = NULL;



Regards,
-Gonglei




--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 3/3] virtio_balloon: Introduce memory recover

2022-05-30 Thread zhenwei pi
+   return;
+
+   in_vbr = >in_vbr;
+   vbr = _vbr->vbr;
+   if (unlikely(vbr->cmd != VIRTIO_BALLOON_R_CMD_RESPONSE))
+   return;
+
+   /* to make sure the contiguous balloon PFNs */
+   for (pfns = 1; pfns < VIRTIO_BALLOON_PAGES_PER_PAGE; pfns++) {
+   pfn0 = virtio32_to_cpu(vb->vdev, in_vbr->pfns[pfns - 1]);
+   pfn1 = virtio32_to_cpu(vb->vdev, in_vbr->pfns[pfns]);
+   if (pfn1 - pfn0 != 1)
+   return;


Yeah, we really shouldn't be dealing with (legacy) 4k PFNs here, but
instead, proper ranges I guess.



MST also pointed out this, I explained in this link:
https://lkml.org/lkml/2022/5/26/942

Rather than page reporting style, virtio-mem style should be fine. Ex,
struct virtio_memory_recover {
__virtio64 addr;
__virtio32 length;
__virtio16 padding[2];
};


+   }
+
+   pfn0 = virtio32_to_cpu(vb->vdev, in_vbr->pfns[0]);
+   if (!pfn_valid(pfn0))
+   return;
+
+   pfn1 = -1;
+   spin_lock(>recover_page_list_lock);
+   list_for_each_entry(page, >corrupted_page_list, lru) {
+   pfn1 = page_to_pfn(page);
+   if (pfn1 == pfn0)
+   break;
+   }
+   spin_unlock(>recover_page_list_lock);
+
+   status = vbr->status;
+   switch (status) {
+   case VIRTIO_BALLOON_R_STATUS_RECOVERED:
+   if (pfn1 == pfn0) {
+   spin_lock(>recover_page_list_lock);
+   list_del(>lru);
+   balloon_page_push(>recovered_page_list, page);


We rather not reuse actual balloon functions in !balloon context. Just
move the page to the proper list directly.



OK.


+   spin_unlock(>recover_page_list_lock);
+   queue_work(system_freezable_wq, 
>unpoison_memory_work);
+   dev_info_ratelimited(>vdev->dev, "recovered pfn 
0x%x", pfn0);


Well, not yet. Shouldn't this go into unpoison_memory_func() ?



OK.

[...]



  
+out_unregister_reporting:

+   if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+   page_reporting_unregister(>pr_dev_info);
  out_unregister_oom:
if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
unregister_oom_notifier(>oom_nb);
@@ -1082,6 +1319,11 @@ static void virtballoon_remove(struct virtio_device 
*vdev)
destroy_workqueue(vb->balloon_wq);
}
  
+	if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_RECOVER)) {


Could the notifier already have been triggered and we might be using the
device before already fully initialized from the notifier and might end
up leaking memory here that we allocated?


+   unregister_memory_failure_notifier(>memory_failure_nb);
+   cancel_work_sync(>unpoison_memory_work);
+   }
+


Could we be leaking memory from the virtballoon_remove() path?



Yes, I'll fix the possible memory leak here.

Thanks a lot.

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-05-30 Thread zhenwei pi




On 5/30/22 15:41, David Hildenbrand wrote:

On 27.05.22 08:32, zhenwei pi wrote:

On 5/27/22 02:37, Peter Xu wrote:

On Wed, May 25, 2022 at 01:16:34PM -0700, Jue Wang wrote:

The hypervisor _must_ emulate poisons identified in guest physical
address space (could be transported from the source VM), this is to
prevent silent data corruption in the guest. With a paravirtual
approach like this patch series, the hypervisor can clear some of the
poisoned HVAs knowing for certain that the guest OS has isolated the
poisoned page. I wonder how much value it provides to the guest if the
guest and workload are _not_ in a pressing need for the extra KB/MB
worth of memory.


I'm curious the same on how unpoisoning could help here.  The reasoning
behind would be great material to be mentioned in the next cover letter.

Shouldn't we consider migrating serious workloads off the host already
where there's a sign of more severe hardware issues, instead?

Thanks,



I'm maintaining 1000,000+ virtual machines, from my experience:
UE is quite unusual and occurs randomly, and I did not hit UE storm case
in the past years. The memory also has no obvious performance drop after
hitting UE.

I hit several CE storm case, the performance memory drops a lot. But I
can't find obvious relationship between UE and CE.

So from the point of my view, to fix the corrupted page for VM seems
good enough. And yes, unpoisoning several pages does not help
significantly, but it is still a chance to make the virtualization better.



I'm curious why we should care about resurrecting a handful of poisoned
pages in a VM. The cover letter doesn't touch on that.

IOW, I'm missing the motivation why we should add additional
code+complexity to unpoison pages at all.

If we're talking about individual 4k pages, it's certainly sub-optimal,
but does it matter in practice? I could understand if we're losing
megabytes of memory. But then, I assume the workload might be seriously
harmed either way already?



Yes, resurrecting a handful of poisoned pages does not help 
significantly. And, in some ways, it seems nice to have. :D


A VM uses RAM of 2M huge page. Once a MCE(@HVAy in [HVAx,HVAz)) occurs, 
the 2M([HVAx,HVAz)) of hypervisor becomes unaccessible, but the guest 
poisons 4K (@GPAy in [GPAx, GPAz)) only, it may hit another 511 MCE 
([GPAx, GPAz) except GPAy). This is the worse case, so I want to add
 '__le32 corrupted_pages' in struct virtio_balloon_config, it is used 
in the next step: reporting 512 * 4K 'corrupted_pages' to the guest, the 
guest has a chance to isolate the other 511 pages ahead of time. And the 
guest actually loses 2M, fixing 512*4K seems to help significantly.




I assume when talking about "the performance memory drops a lot", you
imply that this patch set can mitigate that performance drop?

But why do you see a performance drop? Because we might lose some
possible THP candidates (in the host or the guest) and you want to plug
does holes? I assume you'll see a performance drop simply because
poisoning memory is expensive, including migrating pages around on CE.

If you have some numbers to share, especially before/after this change,
that would be great.



The CE storm leads 2 problems I have even seen:
1, the memory bandwidth slows down to 10%~20%, and the cycles per 
instruction of CPU increases a lot.
2, the THR (/proc/interrupts) interrupts frequently, the CPU has to use 
a lot time to handle IRQ.


But no corrupted page occurs. Migrating VM to another healthy host seems 
a good choice. This patch does not handle CE storm case.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 2/3] mm/memory-failure.c: support reset PTE during unpoison

2022-05-29 Thread zhenwei pi



On 5/30/22 13:02, HORIGUCHI NAOYA(堀口 直也) wrote:

On Fri, May 20, 2022 at 03:06:47PM +0800, zhenwei pi wrote:

Origianlly, unpoison_memory() is only used by hwpoison-inject, and
unpoisons a page which is poisoned by hwpoison-inject too. The kernel PTE
entry has no change during software poison/unpoison.

On a virtualization platform, it's possible to fix hardware corrupted page
by hypervisor, typically the hypervisor remaps the error HVA(host virtual
address). So add a new parameter 'const char *reason' to show the reason
called by.

Once the corrupted page gets fixed, the guest kernel needs put page to
buddy. Reuse the page and hit the following issue(Intel Platinum 8260):
  BUG: unable to handle page fault for address: 888061646000
  #PF: supervisor write access in kernel mode
  #PF: error_code(0x0002) - not-present page
  PGD 2c01067 P4D 2c01067 PUD 61aaa063 PMD 10089b063 PTE 800f9e9b9062
  Oops: 0002 [#1] PREEMPT SMP NOPTI
  CPU: 2 PID: 31106 Comm: stress Kdump: loaded Tainted: G   M   OE 
5.18.0-rc6.bm.1-amd64 #6
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 
rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
  RIP: 0010:clear_page_erms+0x7/0x10

The kernel PTE entry of the fixed page is still uncorrected, kernel hits
page fault during prep_new_page. So add 'bool reset_kpte' to get a change
to fix the PTE entry if the page is fixed by hypervisor.

Signed-off-by: zhenwei pi 
---
  include/linux/mm.h   |  2 +-
  mm/hwpoison-inject.c |  2 +-
  mm/memory-failure.c  | 26 +++---
  3 files changed, 21 insertions(+), 9 deletions(-)



Do you need undoing rate limiting here?  In the original unpoison's usage,
avoiding flood of "Unpoison: Software-unpoisoned page" messages is helpful.

And unpoison seems to be called from virtio-balloon multiple times when
the backend is 2MB hugepages.  If it's right, printing out 512 lines of
"Unpoison: Unpoisoned page 0xXXX by virtio-balloon" messages might not be
so helpful?



All the suggestions(include '[PATCH 1/3] memory-failure: Introduce 
memory failure notifier') are reasonable, I'll fix them in the next 
version. Thanks a lot!



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

[PATCH v8 1/1] crypto: Introduce RSA algorithm

2022-05-27 Thread zhenwei pi
There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Signed-off-by: zhenwei pi 
Signed-off-by: lei he conf.crypto_services =
  1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
 backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
 backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
 /*
  * Set the Maximum length of crypto request.
  * Why this value? Just avoid to overflow when
  * memory allocation for each crypto request.
  */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
 backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
 backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,53 @@ err:
return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return QCRYPTO_HASH_ALG_SHA256;
+
+case VIRTIO_CRYPTO_RSA_SHA512:
+return QCRYPTO_HASH_ALG_SHA512;
+
+default:
+error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+return -1;
+}
+}
+
+static int cryptodev_builtin_set_rsa_options(
+int virtio_padding_algo,
+int virtio_hash_algo,
+QCryptoAkCipherOptionsRSA *opt,
+Error **errp)
+{
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+opt->hash_alg =
+cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
+if (opt->hash_alg < 0) {
+return -1;
+}
+return 0;
+}
+
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+return 0;
+}
+
+error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
+return -1;
+}
+
 static int cryptodev_builtin_create_cipher_session(
 

Introduce akcipher service for virtio-crypto

2022-05-27 Thread zhenwei pi
ernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Zhenwei Pi (1):
  crypto: Introduce RSA algorithm

 backends/cryptodev-builtin.c  | 274 +
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c  |  32 ++-
 hw/virtio/virtio-crypto.c | 323 --
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h|  83 ++--
 6 files changed, 607 insertions(+), 144 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-05-27 Thread zhenwei pi

On 5/27/22 02:37, Peter Xu wrote:

On Wed, May 25, 2022 at 01:16:34PM -0700, Jue Wang wrote:

The hypervisor _must_ emulate poisons identified in guest physical
address space (could be transported from the source VM), this is to
prevent silent data corruption in the guest. With a paravirtual
approach like this patch series, the hypervisor can clear some of the
poisoned HVAs knowing for certain that the guest OS has isolated the
poisoned page. I wonder how much value it provides to the guest if the
guest and workload are _not_ in a pressing need for the extra KB/MB
worth of memory.


I'm curious the same on how unpoisoning could help here.  The reasoning
behind would be great material to be mentioned in the next cover letter.

Shouldn't we consider migrating serious workloads off the host already
where there's a sign of more severe hardware issues, instead?

Thanks,



I'm maintaining 1000,000+ virtual machines, from my experience:
UE is quite unusual and occurs randomly, and I did not hit UE storm case 
in the past years. The memory also has no obvious performance drop after 
hitting UE.


I hit several CE storm case, the performance memory drops a lot. But I 
can't find obvious relationship between UE and CE.


So from the point of my view, to fix the corrupted page for VM seems 
good enough. And yes, unpoisoning several pages does not help 
significantly, but it is still a chance to make the virtualization better.


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: [PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-05-26 Thread zhenwei pi

Hi, Andrew & Naoya

I would appreciate it if you could give me any hint about the changes of 
memory/memory-failure!


On 5/20/22 15:06, zhenwei pi wrote:

Hi,

I'm trying to recover hardware corrupted page by virtio balloon, the
workflow of this feature like this:

Guest  5.MF -> 6.RVQ FE10.Unpoison page
 /   \/
---+-+--+---
| |  |
 4.MCE7.RVQ BE   9.RVQ Event
  QEMU /   \   /
  3.SIGBUS  8.Remap
 /
+
 |
 +--2.MF
  Host   /
1.HW error

1, HardWare page error occurs randomly.
2, host side handles corrupted page by Memory Failure mechanism, sends
SIGBUS to the user process if early-kill is enabled.
3, QEMU handles SIGBUS, if the address belongs to guest RAM, then:
4, QEMU tries to inject MCE into guest.
5, guest handles memory failure again.

1-5 is already supported for a long time, the next steps are supported
in this patch(also related driver patch):

6, guest balloon driver gets noticed of the corrupted PFN, and sends
request to host side by Recover VQ FrontEnd.
7, QEMU handles request from Recover VQ BackEnd, then:
8, QEMU remaps the corrupted HVA fo fix the memory failure, then:
9, QEMU acks the guest side the result by Recover VQ.
10, guest unpoisons the page if the corrupted page gets recoverd
 successfully.

Test:
This patch set can be tested with QEMU(also in developing):
https://github.com/pizhenwei/qemu/tree/balloon-recover

Emulate MCE by QEMU(guest RAM normal page only, hugepage is not supported):
virsh qemu-monitor-command vm --hmp mce 0 9 0xbdc0 0xd 0x61646678 
0x8c

The guest works fine(on Intel Platinum 8260):
  mce: [Hardware Error]: Machine check events logged
  Memory failure: 0x61646: recovery action for dirty LRU page: Recovered
  virtio_balloon virtio5: recovered pfn 0x61646
  Unpoison: Unpoisoned page 0x61646 by virtio-balloon
  MCE: Killing stress:24502 due to hardware memory corruption fault at 
7f5be2e5a010

And the 'HardwareCorrupted' in /proc/meminfo also shows 0 kB.

About the protocol of virtio balloon recover VQ, it's undefined and in
developing currently:
- 'struct virtio_balloon_recover' defines the structure which is used to
   exchange message between guest and host.
- '__le32 corrupted_pages' in struct virtio_balloon_config is used in the next
   step:
   1, a VM uses RAM of 2M huge page, once a MCE occurs, the 2M becomes
  unaccessible. Reporting 512 * 4K 'corrupted_pages' to the guest, the guest
  has a chance to isolate the 512 pages ahead of time.

   2, after migrating to another host, the corrupted pages are actually 
recovered,
  once the guest gets the 'corrupted_pages' with 0, then the guest could
  unpoison all the poisoned pages which are recorded in the balloon driver.

zhenwei pi (3):
   memory-failure: Introduce memory failure notifier
   mm/memory-failure.c: support reset PTE during unpoison
   virtio_balloon: Introduce memory recover

  drivers/virtio/virtio_balloon.c | 243 
  include/linux/mm.h  |   4 +-
  include/uapi/linux/virtio_balloon.h |  16 ++
  mm/hwpoison-inject.c|   2 +-
  mm/memory-failure.c |  59 ++-
  5 files changed, 315 insertions(+), 9 deletions(-)



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 3/3] virtio_balloon: Introduce memory recover

2022-05-26 Thread zhenwei pi

On 5/27/22 03:18, Michael S. Tsirkin wrote:

On Fri, May 20, 2022 at 03:06:48PM +0800, zhenwei pi wrote:

Introduce a new queue 'recover VQ', this allows guest to recover
hardware corrupted page:

Guest  5.MF -> 6.RVQ FE10.Unpoison page
 /   \/
---+-+--+---
| |  |
 4.MCE7.RVQ BE   9.RVQ Event
  QEMU /   \   /
  3.SIGBUS  8.Remap
 /
+
 |
 +--2.MF
  Host   /
1.HW error

The workflow:
1, HardWare page error occurs randomly.
2, host side handles corrupted page by Memory Failure mechanism, sends
SIGBUS to the user process if early-kill is enabled.
3, QEMU handles SIGBUS, if the address belongs to guest RAM, then:
4, QEMU tries to inject MCE into guest.
5, guest handles memory failure again.

1-5 is already supported for a long time, the next steps are supported
in this patch(also related driver patch):
6, guest balloon driver gets noticed of the corrupted PFN, and sends
request to host side by Recover VQ FrontEnd.
7, QEMU handles request from Recover VQ BackEnd, then:
8, QEMU remaps the corrupted HVA fo fix the memory failure, then:
9, QEMU acks the guest side the result by Recover VQ.
10, guest unpoisons the page if the corrupted page gets recoverd
 successfully.

Then the guest fixes the HW page error dynamiclly without rebooting.

Emulate MCE by QEMU, the guest works fine:
  mce: [Hardware Error]: Machine check events logged
  Memory failure: 0x61646: recovery action for dirty LRU page: Recovered
  virtio_balloon virtio5: recovered pfn 0x61646
  Unpoison: Unpoisoned page 0x61646 by virtio-balloon
  MCE: Killing stress:24502 due to hardware memory corruption fault at 
7f5be2e5a010

The 'HardwareCorrupted' in /proc/meminfo also shows 0 kB.

Signed-off-by: zhenwei pi 
---
  drivers/virtio/virtio_balloon.c | 243 
  include/uapi/linux/virtio_balloon.h |  16 ++
  2 files changed, 259 insertions(+)

diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f4c34a2a6b8e..f9d95d1d8a4d 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -52,6 +52,7 @@ enum virtio_balloon_vq {
VIRTIO_BALLOON_VQ_STATS,
VIRTIO_BALLOON_VQ_FREE_PAGE,
VIRTIO_BALLOON_VQ_REPORTING,
+   VIRTIO_BALLOON_VQ_RECOVER,
VIRTIO_BALLOON_VQ_MAX
  };
  
@@ -59,6 +60,12 @@ enum virtio_balloon_config_read {

VIRTIO_BALLOON_CONFIG_READ_CMD_ID = 0,
  };
  
+/* the request body to commucate with host side */

+struct __virtio_balloon_recover {
+   struct virtio_balloon_recover vbr;
+   __virtio32 pfns[VIRTIO_BALLOON_PAGES_PER_PAGE];
+};
+



I don't think this idea of passing 32 bit pfns is going to fly.
What is wrong with just passing the pages normally as a s/g list?
this is what is done for the hints at the moment.

neither should you use __virtio types for new functionality
(should all be __le), nor use __virtio for the struct name.




Guest side sends GPA/PFN to host side by passing the pages normally as a 
s/g list, this is OK.


But in this scenario, guest also needs to get 
status(recovered?corrupted?failed to recover?) of page from the host side.


For a normal page(Ex, 4K), the host could return the status quite 
immediately. But for the 2M hugetlb of guest RAM, the host should be 
pending until the guest requests 512*4K to recover. Once the 2M hugepage 
gets recovered(or failed to recover), the host returns 512 PFNs with 
status to guest. There are at most 512 recover requests of a single 2M 
huge page.


For example, the guest tries to recover a corrupted page:
struct scatterlist status_sg, page_sg, *sgs[2];

sg_init_one(_sg, status, sizeof(*status));
sgs[0] = _sg;

p = page_address(page);
sg_init_one(_sg, p, PAGE_SIZE);
sgs[1] = _sg;

virtqueue_add_sgs(recover_vq, sgs, 1, 1, NULL, GFP_ATOMIC);

The host handles 4K recover request on 2M hugepage, this request is 
pending until the full 2M huge page gets recovered(or failed).


To avoid too many pending request in virt queue, I designed as this 
patch(should use __le), passing PFN in request body, using a single IN 
request only.



...

--- a/include/uapi/linux/virtio_balloon.h
+++ b/include/uapi/linux/virtio_balloon.h
@@ -37,6 +37,7 @@
  #define VIRTIO_BALLOON_F_FREE_PAGE_HINT   3 /* VQ to report free pages */
  #define VIRTIO_BALLOON_F_PAGE_POISON  4 /* Guest is using page poisoning */
  #define VIRTIO_BALLOON_F_REPORTING5 /* Page reporting virtqueue */
+#define VIRTIO_BALLOON_F_RECOVER   6 /* Memory recover virtqueue */
  
  /* Size of a PFN in the balloon interface. */

  #define VIRTIO_BALLOON_PFN_SHIFT 12


Please get this feature recorded in the spec with the virtio TC.
They will also ask you to sup

Re: Re: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto

2022-05-26 Thread zhenwei pi
1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Lei He (6):
   qapi: crypto-akcipher: Introduce akcipher types to qapi
   crypto: add ASN.1 DER decoder
   crypto: Implement RSA algorithm by hogweed
   crypto: Implement RSA algorithm by gcrypt
   test/crypto: Add test suite for crypto akcipher
   tests/crypto: Add test suite for RSA keys

Zhenwei Pi (3):
   virtio-crypto: header update
   crypto: Introduce akcipher crypto class
   crypto: Introduce RSA algorithm

  backends/cryptodev-builtin.c   | 272 ++-
  backends/cryptodev-vhost-user.c|  34 +-
  backends/cryptodev.c   |  32 +-
  crypto/akcipher-gcrypt.c.inc   | 595 +++
  crypto/akcipher-nettle.c.inc   | 451 +++
  crypto/akcipher.c  | 108 +++
  crypto/akcipherpriv.h  |  55 ++
  crypto/der.c   | 189 +
  crypto/der.h   |  81 ++
  crypto/meson.build |   6 +
  crypto/rsakey-builtin.c.inc| 200 +
  crypto/rsakey-nettle.c.inc | 158 
  crypto/rsakey.c|  44 ++
  crypto/rsakey.h|  92 +++
  hw/virtio/virtio-crypto.c  | 323 ++--
  include/crypto/akcipher.h  | 158 
  include/hw/virtio/virtio-crypto.h  |   5 +-
  include/standard-headers/linux/virtio_crypto.h |  82 +-
  include/sysemu/cryptodev.h |  83 ++-
  meson.build|  11 +
  qapi/crypto.json   |  64 ++
  tests/bench/benchmark-crypto-akcipher.c| 137 
  tests/bench/meson.build|   1 +
  tests/bench/test_akcipher_keys.inc | 537 ++
  tests/unit/meson.build |   2 +
  tests/unit/test-crypto-akcipher.c  | 990 +
  tests/unit/test-crypto-der.c   | 290 
  27 files changed, 4854 insertions(+), 146 deletions(-)
  create mode 100644 crypto/akcipher-gcrypt.c.inc
  create mode 100644 crypto/akcipher-nettle.c.inc
  create mode 100644 crypto/akcipher.c
  create mode 100644 crypto/akcipherpriv.h
  create mode 100644 crypto/der.c
  create mode 100644 crypto/der.h
  create mode 100644 crypto/rsakey-builtin.c.inc
  create mode 100644 crypto/rsakey-nettle.c.inc
  create mode 100644 crypto/rsakey.c
  create mode 100644 crypto/rsakey.h
  create mode 100644 include/crypto/akcipher.h
  create mode 100644 tests/bench/benchmark-crypto-akcipher.c
  create mode 100644 tests/bench/test_akcipher_keys.inc
  create mode 100644 tests/unit/test-crypto-akcipher.c
  create mode 100644 tests/unit/test-crypto-der.c

--
2.11.0



With regards,
Daniel


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Re: Re: [PATCH 3/3] virtio_balloon: Introduce memory recover

2022-05-24 Thread zhenwei pi




On 5/25/22 03:35, Sean Christopherson wrote:

On Fri, May 20, 2022, zhenwei pi wrote:

@@ -59,6 +60,12 @@ enum virtio_balloon_config_read {
VIRTIO_BALLOON_CONFIG_READ_CMD_ID = 0,
  };
  
+/* the request body to commucate with host side */

+struct __virtio_balloon_recover {
+   struct virtio_balloon_recover vbr;
+   __virtio32 pfns[VIRTIO_BALLOON_PAGES_PER_PAGE];


I assume this is copied from virtio_balloon.pfns, which also uses __virtio32, 
but
isn't that horribly broken?  PFNs are 'unsigned long', i.e. 64 bits on 64-bit 
kernels.
x86-64 at least most definitely generates 64-bit PFNs.  Unless there's magic I'm
missing, page_to_balloon_pfn() will truncate PFNs and feed the host bad info.



Yes, I also noticed this point, I suppose the balloon device can not 
work on a virtual machine which has physical address larger than 16T.


I still let the recover VQ keep aligned with the inflate VQ and deflate 
VQ. I prefer the recover VQ to be workable/unworkable with 
inflate/deflate VQ together. So I leave this to the virtio balloon 
maintainer to decide ...



@@ -494,6 +511,198 @@ static void update_balloon_size_func(struct work_struct 
*work)
queue_work(system_freezable_wq, work);
  }
  
+/*

+ * virtballoon_memory_failure - notified by memory failure, try to fix the
+ *  corrupted page.
+ * The memory failure notifier is designed to call back when the kernel handled
+ * successfully only, WARN_ON_ONCE on the unlikely condition to find out any
+ * error(memory error handling is a best effort, not 100% coverd).
+ */
+static int virtballoon_memory_failure(struct notifier_block *notifier,
+ unsigned long pfn, void *parm)
+{
+   struct virtio_balloon *vb = container_of(notifier, struct 
virtio_balloon,
+memory_failure_nb);
+   struct page *page;
+   struct __virtio_balloon_recover *out_vbr;
+   struct scatterlist sg;
+   unsigned long flags;
+   int err;
+
+   page = pfn_to_online_page(pfn);
+   if (WARN_ON_ONCE(!page))
+   return NOTIFY_DONE;
+
+   if (PageHuge(page))
+   return NOTIFY_DONE;
+
+   if (WARN_ON_ONCE(!PageHWPoison(page)))
+   return NOTIFY_DONE;
+
+   if (WARN_ON_ONCE(page_count(page) != 1))
+   return NOTIFY_DONE;
+
+   get_page(page); /* balloon reference */
+
+   out_vbr = kzalloc(sizeof(*out_vbr), GFP_KERNEL);
+   if (WARN_ON_ONCE(!out_vbr))
+   return NOTIFY_BAD;


Not that it truly matters, but won't failure at this point leak the poisoned 
page?


I'll fix this, thanks!

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: [PATCH] virtio-crypto: Fix an error handling path in virtio_crypto_alg_skcipher_close_session()

2022-05-22 Thread zhenwei pi




On 5/22/22 21:07, Christophe JAILLET wrote:

Now that a private buffer is allocated (see commit in the Fixes tag),
it must be released in all error handling paths.

Add the missing goto to avoid a leak in the error handling path.

Fixes: 42e6ac99e417 ("virtio-crypto: use private buffer for control request")
Signed-off-by: Christophe JAILLET 
---
  drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
index e553ccadbcbc..e5876286828b 100644
--- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
@@ -239,7 +239,8 @@ static int virtio_crypto_alg_skcipher_close_session(
pr_err("virtio_crypto: Close session failed status: %u, session_id: 
0x%llx\n",
ctrl_status->status, destroy_session->session_id);
  
-		return -EINVAL;

+   err = -EINVAL;
+   goto out;
}
  
  	err = 0;



This looks good to me, thanks!
Acked-by: zhenwei pi 

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH 3/3] virtio_balloon: Introduce memory recover

2022-05-20 Thread zhenwei pi
Introduce a new queue 'recover VQ', this allows guest to recover
hardware corrupted page:

Guest  5.MF -> 6.RVQ FE10.Unpoison page
/   \/
---+-+--+---
   | |  |
4.MCE7.RVQ BE   9.RVQ Event
 QEMU /   \   /
 3.SIGBUS  8.Remap
/
+
|
+--2.MF
 Host   /
   1.HW error

The workflow:
1, HardWare page error occurs randomly.
2, host side handles corrupted page by Memory Failure mechanism, sends
   SIGBUS to the user process if early-kill is enabled.
3, QEMU handles SIGBUS, if the address belongs to guest RAM, then:
4, QEMU tries to inject MCE into guest.
5, guest handles memory failure again.

1-5 is already supported for a long time, the next steps are supported
in this patch(also related driver patch):
6, guest balloon driver gets noticed of the corrupted PFN, and sends
   request to host side by Recover VQ FrontEnd.
7, QEMU handles request from Recover VQ BackEnd, then:
8, QEMU remaps the corrupted HVA fo fix the memory failure, then:
9, QEMU acks the guest side the result by Recover VQ.
10, guest unpoisons the page if the corrupted page gets recoverd
successfully.

Then the guest fixes the HW page error dynamiclly without rebooting.

Emulate MCE by QEMU, the guest works fine:
 mce: [Hardware Error]: Machine check events logged
 Memory failure: 0x61646: recovery action for dirty LRU page: Recovered
 virtio_balloon virtio5: recovered pfn 0x61646
 Unpoison: Unpoisoned page 0x61646 by virtio-balloon
 MCE: Killing stress:24502 due to hardware memory corruption fault at 
7f5be2e5a010

The 'HardwareCorrupted' in /proc/meminfo also shows 0 kB.

Signed-off-by: zhenwei pi 
---
 drivers/virtio/virtio_balloon.c | 243 
 include/uapi/linux/virtio_balloon.h |  16 ++
 2 files changed, 259 insertions(+)

diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f4c34a2a6b8e..f9d95d1d8a4d 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -52,6 +52,7 @@ enum virtio_balloon_vq {
VIRTIO_BALLOON_VQ_STATS,
VIRTIO_BALLOON_VQ_FREE_PAGE,
VIRTIO_BALLOON_VQ_REPORTING,
+   VIRTIO_BALLOON_VQ_RECOVER,
VIRTIO_BALLOON_VQ_MAX
 };
 
@@ -59,6 +60,12 @@ enum virtio_balloon_config_read {
VIRTIO_BALLOON_CONFIG_READ_CMD_ID = 0,
 };
 
+/* the request body to commucate with host side */
+struct __virtio_balloon_recover {
+   struct virtio_balloon_recover vbr;
+   __virtio32 pfns[VIRTIO_BALLOON_PAGES_PER_PAGE];
+};
+
 struct virtio_balloon {
struct virtio_device *vdev;
struct virtqueue *inflate_vq, *deflate_vq, *stats_vq, *free_page_vq;
@@ -126,6 +133,16 @@ struct virtio_balloon {
/* Free page reporting device */
struct virtqueue *reporting_vq;
struct page_reporting_dev_info pr_dev_info;
+
+   /* Memory recover VQ - VIRTIO_BALLOON_F_RECOVER */
+   struct virtqueue *recover_vq;
+   spinlock_t recover_vq_lock;
+   struct notifier_block memory_failure_nb;
+   struct list_head corrupted_page_list;
+   struct list_head recovered_page_list;
+   spinlock_t recover_page_list_lock;
+   struct __virtio_balloon_recover in_vbr;
+   struct work_struct unpoison_memory_work;
 };
 
 static const struct virtio_device_id id_table[] = {
@@ -494,6 +511,198 @@ static void update_balloon_size_func(struct work_struct 
*work)
queue_work(system_freezable_wq, work);
 }
 
+/*
+ * virtballoon_memory_failure - notified by memory failure, try to fix the
+ *  corrupted page.
+ * The memory failure notifier is designed to call back when the kernel handled
+ * successfully only, WARN_ON_ONCE on the unlikely condition to find out any
+ * error(memory error handling is a best effort, not 100% coverd).
+ */
+static int virtballoon_memory_failure(struct notifier_block *notifier,
+ unsigned long pfn, void *parm)
+{
+   struct virtio_balloon *vb = container_of(notifier, struct 
virtio_balloon,
+memory_failure_nb);
+   struct page *page;
+   struct __virtio_balloon_recover *out_vbr;
+   struct scatterlist sg;
+   unsigned long flags;
+   int err;
+
+   page = pfn_to_online_page(pfn);
+   if (WARN_ON_ONCE(!page))
+   return NOTIFY_DONE;
+
+   if (PageHuge(page))
+   return NOTIFY_DONE;
+
+   if (WARN_ON_ONCE(!PageHWPoison(page)))
+   return NOTIFY_DONE;
+
+   if (WARN_ON_ONCE(page_count(page) != 1))
+   return NOTIFY_DONE;
+
+   get_page(page); /* balloon reference */
+
+   out_vbr = kzalloc(sizeof(*out_vbr), GFP_KER

[PATCH 2/3] mm/memory-failure.c: support reset PTE during unpoison

2022-05-20 Thread zhenwei pi
Origianlly, unpoison_memory() is only used by hwpoison-inject, and
unpoisons a page which is poisoned by hwpoison-inject too. The kernel PTE
entry has no change during software poison/unpoison.

On a virtualization platform, it's possible to fix hardware corrupted page
by hypervisor, typically the hypervisor remaps the error HVA(host virtual
address). So add a new parameter 'const char *reason' to show the reason
called by.

Once the corrupted page gets fixed, the guest kernel needs put page to
buddy. Reuse the page and hit the following issue(Intel Platinum 8260):
 BUG: unable to handle page fault for address: 888061646000
 #PF: supervisor write access in kernel mode
 #PF: error_code(0x0002) - not-present page
 PGD 2c01067 P4D 2c01067 PUD 61aaa063 PMD 10089b063 PTE 800f9e9b9062
 Oops: 0002 [#1] PREEMPT SMP NOPTI
 CPU: 2 PID: 31106 Comm: stress Kdump: loaded Tainted: G   M   OE 
5.18.0-rc6.bm.1-amd64 #6
 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 
rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
 RIP: 0010:clear_page_erms+0x7/0x10

The kernel PTE entry of the fixed page is still uncorrected, kernel hits
page fault during prep_new_page. So add 'bool reset_kpte' to get a change
to fix the PTE entry if the page is fixed by hypervisor.

Signed-off-by: zhenwei pi 
---
 include/linux/mm.h   |  2 +-
 mm/hwpoison-inject.c |  2 +-
 mm/memory-failure.c  | 26 +++---
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 665873c2788c..7ba210e86401 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3191,7 +3191,7 @@ enum mf_flags {
 extern int memory_failure(unsigned long pfn, int flags);
 extern void memory_failure_queue(unsigned long pfn, int flags);
 extern void memory_failure_queue_kick(int cpu);
-extern int unpoison_memory(unsigned long pfn);
+extern int unpoison_memory(unsigned long pfn, bool reset_kpte, const char 
*reason);
 extern int sysctl_memory_failure_early_kill;
 extern int sysctl_memory_failure_recovery;
 extern void shake_page(struct page *p);
diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c
index 5c0cddd81505..0dd17ba98ade 100644
--- a/mm/hwpoison-inject.c
+++ b/mm/hwpoison-inject.c
@@ -57,7 +57,7 @@ static int hwpoison_unpoison(void *data, u64 val)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
 
-   return unpoison_memory(val);
+   return unpoison_memory(val, false, "hwpoison-inject");
 }
 
 DEFINE_DEBUGFS_ATTRIBUTE(hwpoison_fops, NULL, hwpoison_inject, "%lli\n");
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 95c218bb0a37..a46de3be1dd7 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -2132,21 +2132,26 @@ core_initcall(memory_failure_init);
 /**
  * unpoison_memory - Unpoison a previously poisoned page
  * @pfn: Page number of the to be unpoisoned page
+ * @reset_kpte: Reset the PTE entry for kmap
+ * @reason: The callers tells why unpoisoning the page
  *
- * Software-unpoison a page that has been poisoned by
- * memory_failure() earlier.
+ * Unpoison a page that has been poisoned by memory_failure() earlier.
  *
- * This is only done on the software-level, so it only works
- * for linux injected failures, not real hardware failures
+ * For linux injected failures, there is no need to reset PTE entry.
+ * It's possible to fix hardware memory failure on a virtualization platform,
+ * once hypervisor fixes the failure, guest needs put page back to buddy and
+ * reset the PTE entry in kernel.
  *
  * Returns 0 for success, otherwise -errno.
  */
-int unpoison_memory(unsigned long pfn)
+int unpoison_memory(unsigned long pfn, bool reset_kpte, const char *reason)
 {
struct page *page;
struct page *p;
int ret = -EBUSY;
int freeit = 0;
+   pte_t *kpte;
+   unsigned long addr;
static DEFINE_RATELIMIT_STATE(unpoison_rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
 
@@ -2208,8 +2213,15 @@ int unpoison_memory(unsigned long pfn)
mutex_unlock(_mutex);
if (!ret || freeit) {
num_poisoned_pages_dec();
-   unpoison_pr_info("Unpoison: Software-unpoisoned page %#lx\n",
-page_to_pfn(p), _rs);
+   pr_info("Unpoison: Unpoisoned page %#lx by %s\n",
+page_to_pfn(p), reason);
+   if (reset_kpte) {
+   preempt_disable();
+   addr = (unsigned long)page_to_virt(p);
+   kpte = virt_to_kpte(addr);
+   set_pte_at(_mm, addr, kpte, pfn_pte(pfn, 
PAGE_KERNEL));
+   preempt_enable();
+   }
}
return ret;
 }
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://li

[PATCH 1/3] memory-failure: Introduce memory failure notifier

2022-05-20 Thread zhenwei pi
Introduce memory failure notifier, once hardware memory failure
occurs, after the kernel handles the corrupted page successfully,
someone who registered this chain gets noticed of the corrupted PFN.

Signed-off-by: zhenwei pi 
---
 include/linux/mm.h  |  2 ++
 mm/memory-failure.c | 33 +
 2 files changed, 35 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 9f44254af8ce..665873c2788c 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3197,6 +3197,8 @@ extern int sysctl_memory_failure_recovery;
 extern void shake_page(struct page *p);
 extern atomic_long_t num_poisoned_pages __read_mostly;
 extern int soft_offline_page(unsigned long pfn, int flags);
+extern int register_memory_failure_notifier(struct notifier_block *nb);
+extern int unregister_memory_failure_notifier(struct notifier_block *nb);
 #ifdef CONFIG_MEMORY_FAILURE
 extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags);
 #else
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 2d590cba412c..95c218bb0a37 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -68,6 +68,35 @@ int sysctl_memory_failure_recovery __read_mostly = 1;
 
 atomic_long_t num_poisoned_pages __read_mostly = ATOMIC_LONG_INIT(0);
 
+static BLOCKING_NOTIFIER_HEAD(mf_notifier_list);
+
+/**
+ * register_memory_failure_notifier - Register function to be called if a
+ *corrupted page gets handled successfully
+ * @nb: Info about notifier function to be called
+ *
+ * Currently always returns zero, as blocking_notifier_chain_register()
+ * always returns zero.
+ */
+int register_memory_failure_notifier(struct notifier_block *nb)
+{
+   return blocking_notifier_chain_register(_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_memory_failure_notifier);
+
+/**
+ * unregister_memory_failure_notifier - Unregister previously registered
+ *  memory failure notifier
+ * @nb: Hook to be unregistered
+ *
+ * Returns zero on success, or %-ENOENT on failure.
+ */
+int unregister_memory_failure_notifier(struct notifier_block *nb)
+{
+   return blocking_notifier_chain_unregister(_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_memory_failure_notifier);
+
 static bool __page_handle_poison(struct page *page)
 {
int ret;
@@ -1136,6 +1165,10 @@ static void action_result(unsigned long pfn, enum 
mf_action_page_type type,
num_poisoned_pages_inc();
pr_err("Memory failure: %#lx: recovery action for %s: %s\n",
pfn, action_page_types[type], action_name[result]);
+
+   /* notify the chain if we handle successfully only */
+   if (result == MF_RECOVERED)
+   blocking_notifier_call_chain(_notifier_list, pfn, NULL);
 }
 
 static int page_action(struct page_state *ps, struct page *p,
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH 0/3] recover hardware corrupted page by virtio balloon

2022-05-20 Thread zhenwei pi
Hi,

I'm trying to recover hardware corrupted page by virtio balloon, the
workflow of this feature like this:

Guest  5.MF -> 6.RVQ FE10.Unpoison page
/   \/
---+-+--+---
   | |  |
4.MCE7.RVQ BE   9.RVQ Event
 QEMU /   \   /
 3.SIGBUS  8.Remap
/
+
|
+--2.MF
 Host   /
   1.HW error

1, HardWare page error occurs randomly.
2, host side handles corrupted page by Memory Failure mechanism, sends
   SIGBUS to the user process if early-kill is enabled.
3, QEMU handles SIGBUS, if the address belongs to guest RAM, then:
4, QEMU tries to inject MCE into guest.
5, guest handles memory failure again.

1-5 is already supported for a long time, the next steps are supported
in this patch(also related driver patch):

6, guest balloon driver gets noticed of the corrupted PFN, and sends
   request to host side by Recover VQ FrontEnd.
7, QEMU handles request from Recover VQ BackEnd, then:
8, QEMU remaps the corrupted HVA fo fix the memory failure, then:
9, QEMU acks the guest side the result by Recover VQ.
10, guest unpoisons the page if the corrupted page gets recoverd
successfully.

Test:
This patch set can be tested with QEMU(also in developing):
https://github.com/pizhenwei/qemu/tree/balloon-recover

Emulate MCE by QEMU(guest RAM normal page only, hugepage is not supported):
virsh qemu-monitor-command vm --hmp mce 0 9 0xbdc0 0xd 0x61646678 
0x8c

The guest works fine(on Intel Platinum 8260):
 mce: [Hardware Error]: Machine check events logged
 Memory failure: 0x61646: recovery action for dirty LRU page: Recovered
 virtio_balloon virtio5: recovered pfn 0x61646
 Unpoison: Unpoisoned page 0x61646 by virtio-balloon
 MCE: Killing stress:24502 due to hardware memory corruption fault at 
7f5be2e5a010

And the 'HardwareCorrupted' in /proc/meminfo also shows 0 kB.

About the protocol of virtio balloon recover VQ, it's undefined and in
developing currently:
- 'struct virtio_balloon_recover' defines the structure which is used to
  exchange message between guest and host.
- '__le32 corrupted_pages' in struct virtio_balloon_config is used in the next
  step:
  1, a VM uses RAM of 2M huge page, once a MCE occurs, the 2M becomes
 unaccessible. Reporting 512 * 4K 'corrupted_pages' to the guest, the guest
 has a chance to isolate the 512 pages ahead of time.

  2, after migrating to another host, the corrupted pages are actually 
recovered,
 once the guest gets the 'corrupted_pages' with 0, then the guest could
 unpoison all the poisoned pages which are recorded in the balloon driver.

zhenwei pi (3):
  memory-failure: Introduce memory failure notifier
  mm/memory-failure.c: support reset PTE during unpoison
  virtio_balloon: Introduce memory recover

 drivers/virtio/virtio_balloon.c | 243 
 include/linux/mm.h  |   4 +-
 include/uapi/linux/virtio_balloon.h |  16 ++
 mm/hwpoison-inject.c|   2 +-
 mm/memory-failure.c |  59 ++-
 5 files changed, 315 insertions(+), 9 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v6 9/9] crypto: Introduce RSA algorithm

2022-05-13 Thread zhenwei pi
There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Signed-off-by: zhenwei pi 
Signed-off-by: lei he conf.crypto_services =
  1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
 backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
 backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
 /*
  * Set the Maximum length of crypto request.
  * Why this value? Just avoid to overflow when
  * memory allocation for each crypto request.
  */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
 backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
 backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,53 @@ err:
return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return QCRYPTO_HASH_ALG_SHA256;
+
+case VIRTIO_CRYPTO_RSA_SHA512:
+return QCRYPTO_HASH_ALG_SHA512;
+
+default:
+error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+return -1;
+}
+}
+
+static int cryptodev_builtin_set_rsa_options(
+int virtio_padding_algo,
+int virtio_hash_algo,
+QCryptoAkCipherOptionsRSA *opt,
+Error **errp)
+{
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+opt->hash_alg =
+cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
+if (opt->hash_alg < 0) {
+return -1;
+}
+return 0;
+}
+
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+return 0;
+}
+
+error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
+return -1;
+}
+
 static int cryptodev_builtin_create_cipher_session(
 

[PATCH v6 8/9] tests/crypto: Add test suite for RSA keys

2022-05-13 Thread zhenwei pi
From: Lei He 

As Daniel suggested, Add tests suite for rsakey, as a way to prove
that we can handle DER errors correctly.

Signed-off-by: zhenwei pi 
Signed-off-by: lei he 
Reviewed-by: Daniel P. Berrangé 
---
 tests/unit/test-crypto-akcipher.c | 285 +-
 1 file changed, 282 insertions(+), 3 deletions(-)

diff --git a/tests/unit/test-crypto-akcipher.c 
b/tests/unit/test-crypto-akcipher.c
index b5be563884..4f1f4214dd 100644
--- a/tests/unit/test-crypto-akcipher.c
+++ b/tests/unit/test-crypto-akcipher.c
@@ -517,6 +517,158 @@ static const uint8_t exp_ciphertext_rsa2048_pkcs1[] = {
 0xd0, 0x28, 0x03, 0x19, 0xa6, 0x06, 0x13, 0x45,
 };
 
+static const uint8_t rsa_private_key_lack_element[] = {
+/* RSAPrivateKey, offset: 0, length: 176 */
+0x30, 0x81, 0xb0,
+/* version, offset: 4, length: 1 */
+0x02, 0x01, 0x00,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* e, offset: 74, length: 3 */
+0x02, 0x03, 0x01, 0x00, 0x01,
+/* d, offset: 79, length: 64 */
+0x02, 0x40,
+0x1e, 0x80, 0xfe, 0xda, 0x65, 0xdb, 0x70, 0xb8,
+0x61, 0x91, 0x28, 0xbf, 0x6c, 0x32, 0xc1, 0x05,
+0xd1, 0x26, 0x6a, 0x1c, 0x83, 0xcc, 0xf4, 0x1f,
+0x53, 0x42, 0x72, 0x1f, 0x62, 0x57, 0x0a, 0xc4,
+0x66, 0x76, 0x30, 0x87, 0xb9, 0xb1, 0xb9, 0x6a,
+0x63, 0xfd, 0x8f, 0x3e, 0xfc, 0x35, 0x3f, 0xd6,
+0x2e, 0x6c, 0xc8, 0x70, 0x8a, 0x17, 0xc1, 0x28,
+0x6a, 0xfe, 0x51, 0x56, 0xb3, 0x92, 0x6f, 0x09,
+/* p, offset: 145, length: 33 */
+0x02, 0x21,
+0x00, 0xe3, 0x2e, 0x2d, 0x8d, 0xba, 0x1c, 0x34,
+0x4c, 0x49, 0x9f, 0xc1, 0xa6, 0xdd, 0xd7, 0x13,
+0x8d, 0x05, 0x48, 0xdd, 0xff, 0x5c, 0x30, 0xbc,
+0x6b, 0xc4, 0x18, 0x9d, 0xfc, 0xa2, 0xd0, 0x9b,
+0x4d,
+/* q, offset: 180, length: 33 */
+0x02, 0x21,
+0x00, 0xd1, 0x75, 0xaf, 0x4b, 0xc6, 0x1a, 0xb0,
+0x98, 0x14, 0x42, 0xae, 0x33, 0xf3, 0x44, 0xde,
+0x21, 0xcb, 0x04, 0xda, 0xfb, 0x1e, 0x35, 0x92,
+0xcd, 0x69, 0xc0, 0x83, 0x06, 0x83, 0x8e, 0x39,
+0x53,
+/* lack element: dp, dq, u */
+};
+
+static const uint8_t rsa_public_key_lack_element[] = {
+/* RSAPublicKey, offset: 0, length: 67 */
+0x30, 0x81, 0x43,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* lack element: e */
+};
+
+static const uint8_t rsa_public_key_empty_element[] = {
+/* RSAPublicKey, offset: 0, length: 69 */
+0x30, 0x81, 0x45,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* e: empty element */
+0x02, 0x00,
+};
+
+static const uint8_t rsa_private_key_empty_element[] = {
+/* RSAPrivateKey, offset: 0, length: 19 */
+0x30, 0x81, 0x13,
+/* version, offset: 4, length: 1 */
+0x02, 0x01, 0x00,
+/* n: empty element */
+0x02, 0x00,
+/* e: empty element */
+0x02, 0x00,
+/* d: empty element */
+0x02, 0x00,
+/* p: empty element */
+0x02, 0x00,
+/* q: empty element */
+0x02, 0x00,
+/* dp: empty element */
+0x02, 0x00,
+/* dq: empty element */
+0x02, 0x00,
+/* u: empty element */
+0x02, 0x00,
+};
+
+static const uint8_t rsa_public_key_invalid_length_val[] = {
+/* RSAPublicKey, INVALID length: 313 */
+0x30, 0x82, 0x01, 0x39,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7

[PATCH v6 7/9] test/crypto: Add test suite for crypto akcipher

2022-05-13 Thread zhenwei pi
From: Lei He 

Add unit test and benchmark test for crypto akcipher.

Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
Reviewed-by: Daniel P. Berrangé 
---
 tests/bench/benchmark-crypto-akcipher.c | 157 ++
 tests/bench/meson.build |   1 +
 tests/bench/test_akcipher_keys.inc  | 537 ++
 tests/unit/meson.build  |   1 +
 tests/unit/test-crypto-akcipher.c   | 711 
 5 files changed, 1407 insertions(+)
 create mode 100644 tests/bench/benchmark-crypto-akcipher.c
 create mode 100644 tests/bench/test_akcipher_keys.inc
 create mode 100644 tests/unit/test-crypto-akcipher.c

diff --git a/tests/bench/benchmark-crypto-akcipher.c 
b/tests/bench/benchmark-crypto-akcipher.c
new file mode 100644
index 00..c6c80c0be1
--- /dev/null
+++ b/tests/bench/benchmark-crypto-akcipher.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU Crypto akcipher speed benchmark
+ *
+ * Copyright (c) 2022 Bytedance
+ *
+ * Authors:
+ *lei he 
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/init.h"
+#include "crypto/akcipher.h"
+#include "standard-headers/linux/virtio_crypto.h"
+
+#include "test_akcipher_keys.inc"
+
+static bool keep_running;
+
+static void alarm_handler(int sig)
+{
+keep_running = false;
+}
+
+static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key,
+size_t keylen,
+QCryptoRSAPaddingAlgorithm padding,
+QCryptoHashAlgorithm hash)
+{
+QCryptoAkCipherOptions opt;
+QCryptoAkCipher *rsa;
+
+opt.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+opt.u.rsa.padding_alg = padding;
+opt.u.rsa.hash_alg = hash;
+rsa = qcrypto_akcipher_new(, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+   priv_key, keylen, _abort);
+return rsa;
+}
+
+static void test_rsa_speed(const uint8_t *priv_key, size_t keylen,
+   size_t key_size)
+{
+#define BYTE 8
+#define SHA1_DGST_LEN 20
+#define DURATION_SECONDS 10
+#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1
+#define HASH QCRYPTO_HASH_ALG_SHA1
+
+g_autoptr(QCryptoAkCipher) rsa =
+create_rsa_akcipher(priv_key, keylen, PADDING, HASH);
+g_autofree uint8_t *dgst = NULL;
+g_autofree uint8_t *signature = NULL;
+size_t count;
+
+dgst = g_new0(uint8_t, SHA1_DGST_LEN);
+memset(dgst, g_test_rand_int(), SHA1_DGST_LEN);
+signature = g_new0(uint8_t, key_size / BYTE);
+
+g_test_message("benchmark rsa%lu (%s-%s) sign in %d seconds", key_size,
+   QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   DURATION_SECONDS);
+alarm(DURATION_SECONDS);
+g_test_timer_start();
+for (keep_running = true, count = 0; keep_running; ++count) {
+g_assert(qcrypto_akcipher_sign(rsa, dgst, SHA1_DGST_LEN,
+   signature, key_size / BYTE,
+   _abort) > 0);
+}
+g_test_timer_elapsed();
+g_test_message("rsa%lu (%s-%s) sign %lu times in %.2f seconds,"
+   " %.2f times/sec ",
+   key_size,  QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   count, g_test_timer_last(),
+   (double)count / g_test_timer_last());
+
+g_test_message("benchmark rsa%lu (%s-%s) verify in %d seconds", key_size,
+   QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   DURATION_SECONDS);
+alarm(DURATION_SECONDS);
+g_test_timer_start();
+for (keep_running = true, count = 0; keep_running; ++count) {
+g_assert(qcrypto_akcipher_verify(rsa, signature, key_size / BYTE,
+ dgst, SHA1_DGST_LEN,
+ _abort) == 0);
+}
+g_test_timer_elapsed();
+g_test_message("rsa%lu (%s-%s) verify %lu times in %.2f seconds,"
+   " %.2f times/sec ",
+   key_size, QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   count, g_test_timer_last(),
+   (double)count / g_test_timer_last());
+}
+
+static void test_rsa_1024_speed(const void *opaque)
+{
+size_t key_size = (size_t)opaque;
+test_rsa_speed(rsa1024_priv_key, sizeof(rsa1024_priv_key), key_size);
+}
+
+static void test_rsa_2048_speed(const void *opaque)
+{
+size_t key_size = (size_t)opaque;
+test_rsa_speed(rsa2048_priv_key, sizeof(rsa2048_priv_key), key_size);
+}
+
+

[PATCH v6 6/9] crypto: Implement RSA algorithm by gcrypt

2022-05-13 Thread zhenwei pi
From: Lei He 

Added gcryt implementation of RSA algorithm, RSA algorithm
implemented by gcrypt has a higher priority than nettle because
it supports raw padding.

Signed-off-by: zhenwei pi 
Signed-off-by: lei he 
---
 crypto/akcipher-gcrypt.c.inc | 597 +++
 crypto/akcipher.c|   4 +-
 2 files changed, 600 insertions(+), 1 deletion(-)
 create mode 100644 crypto/akcipher-gcrypt.c.inc

diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc
new file mode 100644
index 00..6c5daa301e
--- /dev/null
+++ b/crypto/akcipher-gcrypt.c.inc
@@ -0,0 +1,597 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include 
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoGcryptRSA {
+QCryptoAkCipher akcipher;
+gcry_sexp_t key;
+QCryptoRSAPaddingAlgorithm padding_alg;
+QCryptoHashAlgorithm hash_alg;
+} QCryptoGcryptRSA;
+
+static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher)
+{
+QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+if (!rsa) {
+return;
+}
+
+gcry_sexp_release(rsa->key);
+g_free(rsa);
+}
+
+static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
+const QCryptoAkCipherOptionsRSA *opt,
+QCryptoAkCipherKeyType type,
+const uint8_t *key,  size_t keylen,
+Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+switch (opts->alg) {
+case QCRYPTO_AKCIPHER_ALG_RSA:
+return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new(
+>u.rsa, type, key, keylen, errp);
+
+default:
+error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+return NULL;
+}
+
+return NULL;
+}
+
+static void qcrypto_gcrypt_set_rsa_size(QCryptoAkCipher *akcipher, gcry_mpi_t 
n)
+{
+size_t key_size = (gcry_mpi_get_nbits(n) + 7) / 8;
+akcipher->max_plaintext_len = key_size;
+akcipher->max_ciphertext_len = key_size;
+akcipher->max_dgst_len = key_size;
+akcipher->max_signature_len = key_size;
+}
+
+static int qcrypto_gcrypt_parse_rsa_private_key(
+QCryptoGcryptRSA *rsa,
+const uint8_t *key, size_t keylen, Error **errp)
+{
+g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen, errp);
+gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
+bool compute_mul_inv = false;
+int ret = -1;
+gcry_error_t err;
+
+if (!rsa_key) {
+return ret;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->n.data, rsa_key->n.len, NULL);
+if (gcry_err_code(err) != 0) {
+error_setg(errp, "Failed to parse RSA parameter n: %s/%s",
+   gcry_strsource(err), gcry_strerror(err));
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->e.data, rsa_key->e.len, NULL);
+if (gcry_err_code(err) != 0) {
+error_setg(errp, "Failed to parse RSA parameter e: %s/%s",
+   gcry_strsource(err), gcry_strerror(err));
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->d.data, rsa_key->d.len, NULL);
+if (gcry_err_code(err) != 0) {
+error_setg(errp, "Failed to parse RSA parameter d: %s/%s",
+   gcry_strsource(err), gcry_strerror(err));
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->p.data, rsa_key->p.len, NULL);
+if (gcry_err_code(err) != 0) {
+error_setg(errp, "Failed to parse RSA parameter p: %s/%s",
+   gcry_strsource(err), gcry_strerror(err));

[PATCH v6 5/9] crypto: Implement RSA algorithm by hogweed

2022-05-13 Thread zhenwei pi
From: Lei He 

Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
a 'real' RSA backend to handle request from guest side. It's
important to test RSA offload case without OS & hardware requirement.

Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 crypto/akcipher-nettle.c.inc | 451 +++
 crypto/akcipher.c|   4 +
 crypto/meson.build   |   4 +
 crypto/rsakey-builtin.c.inc  | 200 
 crypto/rsakey-nettle.c.inc   | 158 
 crypto/rsakey.c  |  44 
 crypto/rsakey.h  |  94 
 meson.build  |  11 +
 8 files changed, 966 insertions(+)
 create mode 100644 crypto/akcipher-nettle.c.inc
 create mode 100644 crypto/rsakey-builtin.c.inc
 create mode 100644 crypto/rsakey-nettle.c.inc
 create mode 100644 crypto/rsakey.c
 create mode 100644 crypto/rsakey.h

diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
new file mode 100644
index 00..0796bddcaa
--- /dev/null
+++ b/crypto/akcipher-nettle.c.inc
@@ -0,0 +1,451 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include 
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoNettleRSA {
+QCryptoAkCipher akcipher;
+struct rsa_public_key pub;
+struct rsa_private_key priv;
+QCryptoRSAPaddingAlgorithm padding_alg;
+QCryptoHashAlgorithm hash_alg;
+} QCryptoNettleRSA;
+
+static void qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher)
+{
+QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+if (!rsa) {
+return;
+}
+
+rsa_public_key_clear(>pub);
+rsa_private_key_clear(>priv);
+g_free(rsa);
+}
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+const QCryptoAkCipherOptionsRSA *opt,
+QCryptoAkCipherKeyType type,
+const uint8_t *key,  size_t keylen,
+Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+switch (opts->alg) {
+case QCRYPTO_AKCIPHER_ALG_RSA:
+return qcrypto_nettle_rsa_new(>u.rsa, type, key, keylen, errp);
+
+default:
+error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+return NULL;
+}
+
+return NULL;
+}
+
+static void qcrypto_nettle_rsa_set_akcipher_size(QCryptoAkCipher *akcipher,
+ int key_size)
+{
+akcipher->max_plaintext_len = key_size;
+akcipher->max_ciphertext_len = key_size;
+akcipher->max_signature_len = key_size;
+akcipher->max_dgst_len = key_size;
+}
+
+static int qcrypt_nettle_parse_rsa_private_key(QCryptoNettleRSA *rsa,
+   const uint8_t *key,
+   size_t keylen,
+   Error **errp)
+{
+g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen, errp);
+
+if (!rsa_key) {
+return -1;
+}
+
+nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.d, rsa_key->d.len, 
rsa_key->d.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.p, rsa_key->p.len, 
rsa_key->p.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.q, rsa_key->q.len, 
rsa_key->q.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.a, rsa_key->dp.len,
+  rsa_key->dp.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.b, rsa_key->dq.len,
+  rsa_key->dq.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.c, rsa_key->u.len

[PATCH v6 4/9] crypto: add ASN.1 DER decoder

2022-05-13 Thread zhenwei pi
From: Lei He 

Add an ANS.1 DER decoder which is used to parse asymmetric
cipher keys

Signed-off-by: zhenwei pi 
Signed-off-by: lei he 
---
 crypto/der.c | 189 +++
 crypto/der.h |  81 ++
 crypto/meson.build   |   1 +
 tests/unit/meson.build   |   1 +
 tests/unit/test-crypto-der.c | 290 +++
 5 files changed, 562 insertions(+)
 create mode 100644 crypto/der.c
 create mode 100644 crypto/der.h
 create mode 100644 tests/unit/test-crypto-der.c

diff --git a/crypto/der.c b/crypto/der.c
new file mode 100644
index 00..721bbb3317
--- /dev/null
+++ b/crypto/der.c
@@ -0,0 +1,189 @@
+/*
+ * QEMU Crypto ASN.1 DER decoder
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/der.h"
+
+enum QCryptoDERTypeTag {
+QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
+QCRYPTO_DER_TYPE_TAG_INT = 0x2,
+QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
+QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
+QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
+QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
+QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
+QCRYPTO_DER_TYPE_TAG_SET = 0x11,
+};
+
+#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
+#define QCRYPTO_DER_SHORT_LEN_MASK 0x80
+
+static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
+{
+return **data;
+}
+
+static void qcrypto_der_cut_nbytes(const uint8_t **data,
+   size_t *dlen,
+   size_t nbytes)
+{
+*data += nbytes;
+*dlen -= nbytes;
+}
+
+static uint8_t qcrypto_der_cut_byte(const uint8_t **data, size_t *dlen)
+{
+uint8_t val = qcrypto_der_peek_byte(data, dlen);
+
+qcrypto_der_cut_nbytes(data, dlen, 1);
+
+return val;
+}
+
+static int qcrypto_der_invoke_callback(QCryptoDERDecodeCb cb, void *ctx,
+   const uint8_t *value, size_t vlen,
+   Error **errp)
+{
+if (!cb) {
+return 0;
+}
+
+return cb(ctx, value, vlen, errp);
+}
+
+static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t 
*dlen,
+ QCryptoDERDecodeCb cb, void *ctx,
+ Error **errp)
+{
+const uint8_t *value;
+size_t vlen = 0;
+uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);
+
+/* short format of definite-length */
+if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
+if (byte_count > *dlen) {
+error_setg(errp, "Invalid content length: %u", byte_count);
+return -1;
+}
+
+value = *data;
+vlen = byte_count;
+qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+return -1;
+}
+return vlen;
+}
+
+/* Ignore highest bit */
+byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;
+
+/*
+ * size_t is enough to store the value of length, although the DER
+ * encoding standard supports larger length.
+ */
+if (byte_count > sizeof(size_t)) {
+error_setg(errp, "Invalid byte count of content length: %u",
+   byte_count);
+return -1;
+}
+
+if (byte_count > *dlen) {
+error_setg(errp, "Invalid content length: %u", byte_count);
+return -1;
+}
+while (byte_count--) {
+vlen <<= 8;
+vlen += qcrypto_der_cut_byte(data, dlen);
+}
+
+if (vlen > *dlen) {
+error_setg(errp, "Invalid content length: %lu", vlen);
+return -1;
+}
+
+value = *data;
+qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+return -1;
+}
+return vlen;
+}
+
+static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
+QCryptoDERDecodeCb cb, void *ctx,
+Error **errp)
+{
+uint8_t val;
+if (*dlen < 1) {
+error_setg(errp, "Need more data");
+return -1;
+}
+val = qcrypto_der_peek_

[PATCH v6 3/9] crypto: Introduce akcipher crypto class

2022-05-13 Thread zhenwei pi
Introduce new akcipher crypto class 'QCryptoAkCIpher', which supports
basic asymmetric operations: encrypt, decrypt, sign and verify.

Suggested by Daniel P. Berrangé, also add autoptr cleanup for the new
class. Thanks to Daniel!

Co-developed-by: lei he 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/akcipher.c | 102 
 crypto/akcipherpriv.h |  55 +
 crypto/meson.build|   1 +
 include/crypto/akcipher.h | 158 ++
 4 files changed, 316 insertions(+)
 create mode 100644 crypto/akcipher.c
 create mode 100644 crypto/akcipherpriv.h
 create mode 100644 include/crypto/akcipher.h

diff --git a/crypto/akcipher.c b/crypto/akcipher.c
new file mode 100644
index 00..ab28bf415b
--- /dev/null
+++ b/crypto/akcipher.c
@@ -0,0 +1,102 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/akcipher.h"
+#include "akcipherpriv.h"
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+QCryptoAkCipher *akcipher = NULL;
+
+return akcipher;
+}
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+return false;
+}
+
+int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
+ const void *in, size_t in_len,
+ void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->encrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_decrypt(QCryptoAkCipher *akcipher,
+ const void *in, size_t in_len,
+ void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->decrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_sign(QCryptoAkCipher *akcipher,
+  const void *in, size_t in_len,
+  void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->sign(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_verify(QCryptoAkCipher *akcipher,
+const void *in, size_t in_len,
+const void *in2, size_t in2_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->verify(akcipher, in, in_len, in2, in2_len, errp);
+}
+
+int qcrypto_akcipher_max_plaintext_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_plaintext_len;
+}
+
+int qcrypto_akcipher_max_ciphertext_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_ciphertext_len;
+}
+
+int qcrypto_akcipher_max_signature_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_signature_len;
+}
+
+int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_dgst_len;
+}
+
+void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+drv->free(akcipher);
+}
diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h
new file mode 100644
index 00..739f639bcf
--- /dev/null
+++ b/crypto/akcipherpriv.h
@@ -0,0 +1,55 @@
+/*
+ * QEMU Crypto asymmetric algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have receiv

[PATCH v6 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi

2022-05-13 Thread zhenwei pi
From: Lei He 

Introduce akcipher types, also include RSA related types.

Reviewed-by: Daniel P. Berrangé 
Signed-off-by: Lei He 
Signed-off-by: zhenwei pi 
---
 qapi/crypto.json | 64 
 1 file changed, 64 insertions(+)

diff --git a/qapi/crypto.json b/qapi/crypto.json
index 1ec54c15ca..f7bb9a42d0 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -540,3 +540,67 @@
   'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] },
 '*sanity-check': 'bool',
 '*passwordid': 'str' } }
+##
+# @QCryptoAkCipherAlgorithm:
+#
+# The supported algorithms for asymmetric encryption ciphers
+#
+# @rsa: RSA algorithm
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherAlgorithm',
+  'prefix': 'QCRYPTO_AKCIPHER_ALG',
+  'data': ['rsa']}
+
+##
+# @QCryptoAkCipherKeyType:
+#
+# The type of asymmetric keys.
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherKeyType',
+  'prefix': 'QCRYPTO_AKCIPHER_KEY_TYPE',
+  'data': ['public', 'private']}
+
+##
+# @QCryptoRSAPaddingAlgorithm:
+#
+# The padding algorithm for RSA.
+#
+# @raw: no padding used
+# @pkcs1: pkcs1#v1.5
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoRSAPaddingAlgorithm',
+  'prefix': 'QCRYPTO_RSA_PADDING_ALG',
+  'data': ['raw', 'pkcs1']}
+
+##
+# @QCryptoAkCipherOptionsRSA:
+#
+# Specific parameters for RSA algorithm.
+#
+# @hash-alg: QCryptoHashAlgorithm
+# @padding-alg: QCryptoRSAPaddingAlgorithm
+#
+# Since: 7.1
+##
+{ 'struct': 'QCryptoAkCipherOptionsRSA',
+  'data': { 'hash-alg':'QCryptoHashAlgorithm',
+'padding-alg': 'QCryptoRSAPaddingAlgorithm'}}
+
+##
+# @QCryptoAkCipherOptions:
+#
+# The options that are available for all asymmetric key algorithms
+# when creating a new QCryptoAkCipher.
+#
+# Since: 7.1
+##
+{ 'union': 'QCryptoAkCipherOptions',
+  'base': { 'alg': 'QCryptoAkCipherAlgorithm' },
+  'discriminator': 'alg',
+  'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

[PATCH v6 1/9] virtio-crypto: header update

2022-05-13 Thread zhenwei pi
Update header from linux, support akcipher service.

Reviewed-by: Daniel P. Berrangé 
Reviewed-by: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 .../standard-headers/linux/virtio_crypto.h| 82 ++-
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/include/standard-headers/linux/virtio_crypto.h 
b/include/standard-headers/linux/virtio_crypto.h
index 5ff0b4ee59..68066dafb6 100644
--- a/include/standard-headers/linux/virtio_crypto.h
+++ b/include/standard-headers/linux/virtio_crypto.h
@@ -37,6 +37,7 @@
 #define VIRTIO_CRYPTO_SERVICE_HASH   1
 #define VIRTIO_CRYPTO_SERVICE_MAC2
 #define VIRTIO_CRYPTO_SERVICE_AEAD   3
+#define VIRTIO_CRYPTO_SERVICE_AKCIPHER 4
 
 #define VIRTIO_CRYPTO_OPCODE(service, op)   (((service) << 8) | (op))
 
@@ -57,6 +58,10 @@ struct virtio_crypto_ctrl_header {
   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x02)
 #define VIRTIO_CRYPTO_AEAD_DESTROY_SESSION \
   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x03)
+#define VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION \
+  VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x04)
+#define VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION \
+  VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x05)
uint32_t opcode;
uint32_t algo;
uint32_t flag;
@@ -180,6 +185,58 @@ struct virtio_crypto_aead_create_session_req {
uint8_t padding[32];
 };
 
+struct virtio_crypto_rsa_session_para {
+#define VIRTIO_CRYPTO_RSA_RAW_PADDING   0
+#define VIRTIO_CRYPTO_RSA_PKCS1_PADDING 1
+   uint32_t padding_algo;
+
+#define VIRTIO_CRYPTO_RSA_NO_HASH   0
+#define VIRTIO_CRYPTO_RSA_MD2   1
+#define VIRTIO_CRYPTO_RSA_MD3   2
+#define VIRTIO_CRYPTO_RSA_MD4   3
+#define VIRTIO_CRYPTO_RSA_MD5   4
+#define VIRTIO_CRYPTO_RSA_SHA1  5
+#define VIRTIO_CRYPTO_RSA_SHA2566
+#define VIRTIO_CRYPTO_RSA_SHA3847
+#define VIRTIO_CRYPTO_RSA_SHA5128
+#define VIRTIO_CRYPTO_RSA_SHA2249
+   uint32_t hash_algo;
+};
+
+struct virtio_crypto_ecdsa_session_para {
+#define VIRTIO_CRYPTO_CURVE_UNKNOWN   0
+#define VIRTIO_CRYPTO_CURVE_NIST_P192 1
+#define VIRTIO_CRYPTO_CURVE_NIST_P224 2
+#define VIRTIO_CRYPTO_CURVE_NIST_P256 3
+#define VIRTIO_CRYPTO_CURVE_NIST_P384 4
+#define VIRTIO_CRYPTO_CURVE_NIST_P521 5
+   uint32_t curve_id;
+   uint32_t padding;
+};
+
+struct virtio_crypto_akcipher_session_para {
+#define VIRTIO_CRYPTO_NO_AKCIPHER0
+#define VIRTIO_CRYPTO_AKCIPHER_RSA   1
+#define VIRTIO_CRYPTO_AKCIPHER_DSA   2
+#define VIRTIO_CRYPTO_AKCIPHER_ECDSA 3
+   uint32_t algo;
+
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC  1
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE 2
+   uint32_t keytype;
+   uint32_t keylen;
+
+   union {
+   struct virtio_crypto_rsa_session_para rsa;
+   struct virtio_crypto_ecdsa_session_para ecdsa;
+   } u;
+};
+
+struct virtio_crypto_akcipher_create_session_req {
+   struct virtio_crypto_akcipher_session_para para;
+   uint8_t padding[36];
+};
+
 struct virtio_crypto_alg_chain_session_para {
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER  1
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH  2
@@ -247,6 +304,8 @@ struct virtio_crypto_op_ctrl_req {
mac_create_session;
struct virtio_crypto_aead_create_session_req
aead_create_session;
+   struct virtio_crypto_akcipher_create_session_req
+   akcipher_create_session;
struct virtio_crypto_destroy_session_req
destroy_session;
uint8_t padding[56];
@@ -266,6 +325,14 @@ struct virtio_crypto_op_header {
VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x00)
 #define VIRTIO_CRYPTO_AEAD_DECRYPT \
VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_ENCRYPT \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00)
+#define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_SIGN \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02)
+#define VIRTIO_CRYPTO_AKCIPHER_VERIFY \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x03)
uint32_t opcode;
/* algo should be service-specific algorithms */
uint32_t algo;
@@ -390,6 +457,16 @@ struct virtio_crypto_aead_data_req {
uint8_t padding[32];
 };
 
+struct virtio_crypto_akcipher_para {
+   uint32_t src_data_len;
+   uint32_t dst_data_len;
+};
+
+struct virtio_crypto_akcipher_data_req {
+   struct virtio_crypto_akcipher_para para;
+   uint8_t padding[40];
+};
+
 /* The request of the data virtqueue's packet */
 struct virtio_crypto_op_data_req {
struct virtio_crypto_op_header header;
@@ -399,6 +476,7 @@ struct virtio_crypto_op_data_req {

[PATCH v6 0/9] Introduce akcipher service for virtio-crypto

2022-05-13 Thread zhenwei pi
v5 -> v6:
- Fix build errors and codestyles.
- Add parameter 'Error **errp' for qcrypto_akcipher_rsakey_parse.
- Report more detailed errors.
- Fix buffer length check and return values of akcipher-nettle, allows caller to
  pass a buffer with larger size than actual needed.

A million thanks to Daniel!

v4 -> v5:
- Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
- Rename asn1_decoder.c to der.c.
- Code style fix: use 'cleanup' & 'error' lables.
- Allow autoptr type to auto-free.
- Add test cases for rsakey to handle DER error.
- Other minor fixes.

v3 -> v4:
- Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
XXX-alg -> XXX-algo.
- Change version info in qapi/crypto.json, from 7.0 -> 7.1.
- Remove ecdsa from qapi/crypto.json, it would be introduced with the 
implemetion later.
- Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in 
qapi/crypto.json.
- Rename arguments of qcrypto_akcipher_XXX to keep aligned with 
qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add 
qcrypto_akcipher_max_XXX APIs.
- Add new API: qcrypto_akcipher_supports.
- Change the return value of qcrypto_akcipher_enc/dec/sign, these functions 
return the actual length of result.
- Separate ASN.1 source code and test case clean.
- Disable RSA raw encoding for akcipher-nettle.
- Separate RSA key parser into rsakey.{hc}, and implememts it with 
builtin-asn1-decoder and nettle respectivly.
- Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher 
priority than nettle.
- For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of 
returned result maybe less than the dst buffer size, return the actual length 
of result instead of the buffer length to the guest side. (in function 
virtio_crypto_akcipher_input_data_helper)
- Other minor changes.

Thanks to Daniel!

Eric pointed out this missing part of use case, send it here again.

In our plan, the feature is designed for HTTPS offloading case and other 
applications which use kernel RSA/ecdsa by keyctl syscall. The full picture 
shows bellow:


  Nginx/openssl[1] ... Apps
Guest   -
   virtio-crypto driver[2]
-
   virtio-crypto backend[3]
Host-
  /  |  \
  builtin[4]   vhost keyctl[5] ...


[1] User applications can offload RSA calculation to kernel by keyctl syscall. 
There is no keyctl engine in openssl currently, we developed a engine and tried 
to contribute it to openssl upstream, but openssl 1.x does not accept new 
feature. Link:
https://github.com/openssl/openssl/pull/16689

This branch is available and maintained by Lei 
https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine

We tested nginx(change config file only) with openssl keyctl engine, it works 
fine.

[2] virtio-crypto driver is used to communicate with host side, send requests 
to host side to do asymmetric calculation.
https://lkml.org/lkml/2022/3/1/1425

[3] virtio-crypto backend handles requests from guest side, and forwards 
request to crypto backend driver of QEMU.

[4] Currently RSA is supported only in builtin driver. This driver is supposed 
to test the full feature without other software(Ex vhost process) and hardware 
dependence. ecdsa is introduced into qapi type without implementation, this may 
be implemented in Q3-2022 or later. If ecdsa type definition should be added 
with the implementation together, I'll remove this in next version.

[5] keyctl backend is in development, we will post this feature in Q2-2022. 
keyctl backend can use hardware acceleration(Ex, Intel QAT).

Setup the full environment, tested with Intel QAT on host side, the QPS of 
HTTPS increase to ~200% in a guest.

VS PCI passthrough: the most important benefit of this solution makes the VM 
migratable.

v2 -> v3:
- Introduce akcipher types to qapi
- Add test/benchmark suite for akcipher class
- Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
  - crypto: Introduce akcipher crypto class
  - virtio-crypto: Introduce RSA algorithm

v1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Lei He (6):
  qapi: crypto-akcipher: Introduce akcipher types to qapi
  crypto: add ASN.1 DER decoder
  crypto: Implement RSA algorithm by hogweed
  crypto: Implement RSA algorithm by gcrypt
  test/crypto: Add test suite for crypto akcipher
  tests/crypto: Add test suite for RSA keys

Zhenwei Pi (3):
  virtio-crypto: header update
  crypto: Introduce akcipher crypto class
  crypto: Introduce RSA algorithm

 backends/cryptodev-builtin.c  | 272 +

Re: Re: [PATCH v5 1/9] virtio-crypto: header update

2022-05-12 Thread zhenwei pi

Hi, Daniel

Something I do in my local branch(for the v6 series):
- [PATCH v5 1/9] virtio-crypto: header update
- [PATCH v5 3/9] crypto: Introduce akcipher crypto class
  Add 'Reviewed-by: Daniel P. Berrangé '

- [PATCH v5 4/9] crypto: add ASN.1 DER decoder
- [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher
  Fixed the issues you pointed out.

Do you have suggestions about the other patches? Or I'll send the v6 series?

On 5/12/22 17:55, Daniel P. Berrangé wrote:

On Thu, Apr 28, 2022 at 09:59:35PM +0800, zhenwei pi wrote:

Update header from linux, support akcipher service.

Reviewed-by: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
  .../standard-headers/linux/virtio_crypto.h| 82 ++-
  1 file changed, 81 insertions(+), 1 deletion(-)


I see these changes were now merged in linux.git with

   commit 24e19590628b58578748eeaec8140bf9c9dc00d9
   Author: zhenwei pi 
   AuthorDate: Wed Mar 2 11:39:15 2022 +0800
   Commit: Michael S. Tsirkin 
   CommitDate: Mon Mar 28 16:52:58 2022 -0400

 virtio-crypto: introduce akcipher service
 
 Introduce asymmetric service definition, asymmetric operations and

 several well known algorithms.
 
 Co-developed-by: lei he 

 Signed-off-by: lei he 
 Signed-off-by: zhenwei pi 
 Link: 
https://lore.kernel.org/r/20220302033917.1295334-3-pizhen...@bytedance.com
 Signed-off-by: Michael S. Tsirkin 
 Reviewed-by: Gonglei 


And the changes proposed here match that, so

   Reviewed-by: Daniel P. Berrangé 


With regards,
Daniel


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

PING: [PATCH v6 0/5] virtio-crypto: Improve performance

2022-05-10 Thread zhenwei pi

Hi, Michael

I would appreciate it if you could review this series!

On 5/6/22 21:16, zhenwei pi wrote:

v5 -> v6:
  - Minor fix for crypto_engine_alloc_init_and_set().
  - All the patches have been reviewed by Gonglei, add this in patch.
  Thanks to Gonglei.

v4 -> v5:
  - Fix potentially dereferencing uninitialized variables in
'virtio-crypto: use private buffer for control request'.
Thanks to Dan Carpenter!

v3 -> v4:
  - Don't create new file virtio_common.c, the new functions are added
into virtio_crypto_core.c
  - Split the first patch into two parts:
  1, change code style,
  2, use private buffer instead of shared buffer
  - Remove relevant change.
  - Other minor changes.

v2 -> v3:
  - Jason suggested that spliting the first patch into two part:
  1, using private buffer
  2, remove the busy polling
Rework as Jason's suggestion, this makes the smaller change in
each one and clear.

v1 -> v2:
  - Use kfree instead of kfree_sensitive for insensitive buffer.
  - Several coding style fix.
  - Use memory from current node, instead of memory close to device
  - Add more message in commit, also explain why removing per-device
request buffer.
  - Add necessary comment in code to explain why using kzalloc to
allocate struct virtio_crypto_ctrl_request.

v1:
The main point of this series is to improve the performance for
virtio crypto:
- Use wait mechanism instead of busy polling for ctrl queue, this
   reduces CPU and lock racing, it's possiable to create/destroy session
   parallelly, QPS increases from ~40K/s to ~200K/s.
- Enable retry on crypto engine to improve performance for data queue,
   this allows the larger depth instead of 1.
- Fix dst data length in akcipher service.
- Other style fix.

lei he (2):
   virtio-crypto: adjust dst_len at ops callback
   virtio-crypto: enable retry for virtio-crypto-dev

zhenwei pi (3):
   virtio-crypto: change code style
   virtio-crypto: use private buffer for control request
   virtio-crypto: wait ctrl queue instead of busy polling

  .../virtio/virtio_crypto_akcipher_algs.c  |  95 ++--
  drivers/crypto/virtio/virtio_crypto_common.h  |  21 ++-
  drivers/crypto/virtio/virtio_crypto_core.c|  55 ++-
  .../virtio/virtio_crypto_skcipher_algs.c  | 140 --
  4 files changed, 182 insertions(+), 129 deletions(-)



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


PING: [PATCH v5 9/9] crypto: Introduce RSA algorithm

2022-05-08 Thread zhenwei pi

Hi, Lei

I would appreciate it if you could review this patch!

On 4/28/22 21:59, zhenwei pi wrote:

There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
   - both public key & private key
   - create/close session
   - encrypt/decrypt/sign/verify basic driver operation
   - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Signed-off-by: zhenwei pi 
Signed-off-by: lei he   
  
@@ -41,11 +42,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN)

  typedef struct CryptoDevBackendBuiltinSession {
  QCryptoCipher *cipher;
  uint8_t direction; /* encryption or decryption */
-uint8_t type; /* cipher? hash? aead? */
+uint8_t type; /* cipher? hash? aead? akcipher? */
+QCryptoAkCipher *akcipher;
  QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
  } CryptoDevBackendBuiltinSession;
  
-/* Max number of symmetric sessions */

+/* Max number of symmetric/asymmetric sessions */
  #define MAX_NUM_SESSIONS 256
  
  #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN512

@@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
  backend->conf.crypto_services =
   1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
   1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
  backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
  backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
  /*
   * Set the Maximum length of crypto request.
   * Why this value? Just avoid to overflow when
   * memory allocation for each crypto request.
   */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
  backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
  backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
  
@@ -148,6 +152,53 @@ err:

 return -1;
  }
  
+static int cryptodev_builtin_get_rsa_hash_algo(

+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return QCRYPTO_HASH_ALG_SHA256;
+
+case VIRTIO_CRYPTO_RSA_SHA512:
+return QCRYPTO_HASH_ALG_SHA512;
+
+default:
+error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+return -1;
+}
+}
+
+static int cryptodev

Re: Re: Re: [PATCH 3/4] mm/memofy-failure.c: optimize hwpoison_filter

2022-05-07 Thread zhenwei pi

On 5/7/22 16:20, Naoya Horiguchi wrote:

On Sat, May 07, 2022 at 08:28:05AM +0800, zhenwei pi wrote:


On 5/7/22 00:28, David Hildenbrand wrote:

On 06.05.22 15:38, zhenwei pi wrote:



On 5/6/22 16:59, Naoya Horiguchi wrote:

On Fri, Apr 29, 2022 at 10:22:05PM +0800, zhenwei pi wrote:

In the memory failure procedure, hwpoison_filter has higher priority,
if memory_filter() filters the error event, there is no need to do
the further work.


Could you clarify what problem you are trying to solve (what does
"optimize" mean in this context or what is the benefit)?



OK. The background of this work:
As well known, the memory failure mechanism handles memory corrupted
event, and try to send SIGBUS to the user process which uses this
corrupted page.

For the virtualization case, QEMU catches SIGBUS and tries to inject MCE
into the guest, and the guest handles memory failure again. Thus the
guest gets the minimal effect from hardware memory corruption.

The further step I'm working on:
1, try to modify code to decrease poisoned pages in a single place
(mm/memofy-failure.c: simplify num_poisoned_pages_dec in this series).


This is fine to me.



2, try to use page_handle_poison() to handle SetPageHWPoison() and
num_poisoned_pages_inc() together. It would be best to call
num_poisoned_pages_inc() in a single place too. I'm not sure if this is
possible or not, please correct me if I misunderstand.


SetPageHWPoison() can be cancelled in memory_failure(), so simply bundling
it with num_poisoned_pages_inc() might not be optimal.  I think that
action_result() is supposed to be called when memory error handling is
effective (not filtered, not cancelled). So moving num_poisoned_pages_inc()
(and notification code in your plan) into this function might be good.

OK, I'll remove this patch(mm/memofy-failure.c: optimize 
hwpoison_filter) from this series, and fix the other 3 patches in the v2 
version. Then try to implement/test as your suggestion in another series.




3, introduce memory failure notifier list in memory-failure.c: notify
the corrupted PFN to someone who registers this list.
If I can complete [1] and [2] part, [3] will be quite easy(just call
notifier list after increasing poisoned page).

4, introduce memory recover VQ for memory balloon device, and registers
memory failure notifier list. During the guest kernel handles memory
failure, balloon device gets notified by memory failure notifier list,
and tells the host to recover the corrupted PFN(GPA) by the new VQ.


Most probably you might want to do that asynchronously, and once the
callback succeeds, un-poison the page.


Yes!



5, host side remaps the corrupted page(HVA), and tells the guest side to
unpoison the PFN(GPA). Then the guest fixes the corrupted page(GPA)
dynamically.


I think QEMU already does that during reboots. Now it would be triggered
by the guest for individual pages.


Yes, currently QEMU supports to un-poison corrupted pages during
reset/reboot. We can reuse some code to do the work in this case, this
allows a VM to fix corrupted pages as soon as possible(also no need to
reset/reboot).


So this finally allows to replace broken page mapped to guest with
a healthy page without rebooting the guest. That sounds helpful.

Thanks,
Naoya Horiguchi


Yes, it's my plan. Thanks for your suggestions!





Because [4] and [5] are related to balloon device, also CC Michael,
David and Jason.


Doesn't sound too crazy for me, although it's a shame that we always
have to use virtio-balloon for such fairly balloon-unrelated things.


Thanks!

--
zhenwei pi


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 3/4] mm/memofy-failure.c: optimize hwpoison_filter

2022-05-06 Thread zhenwei pi



On 5/7/22 00:28, David Hildenbrand wrote:

On 06.05.22 15:38, zhenwei pi wrote:



On 5/6/22 16:59, Naoya Horiguchi wrote:

On Fri, Apr 29, 2022 at 10:22:05PM +0800, zhenwei pi wrote:

In the memory failure procedure, hwpoison_filter has higher priority,
if memory_filter() filters the error event, there is no need to do
the further work.


Could you clarify what problem you are trying to solve (what does
"optimize" mean in this context or what is the benefit)?



OK. The background of this work:
As well known, the memory failure mechanism handles memory corrupted
event, and try to send SIGBUS to the user process which uses this
corrupted page.

For the virtualization case, QEMU catches SIGBUS and tries to inject MCE
into the guest, and the guest handles memory failure again. Thus the
guest gets the minimal effect from hardware memory corruption.

The further step I'm working on:
1, try to modify code to decrease poisoned pages in a single place
(mm/memofy-failure.c: simplify num_poisoned_pages_dec in this series).

2, try to use page_handle_poison() to handle SetPageHWPoison() and
num_poisoned_pages_inc() together. It would be best to call
num_poisoned_pages_inc() in a single place too. I'm not sure if this is
possible or not, please correct me if I misunderstand.

3, introduce memory failure notifier list in memory-failure.c: notify
the corrupted PFN to someone who registers this list.
If I can complete [1] and [2] part, [3] will be quite easy(just call
notifier list after increasing poisoned page).

4, introduce memory recover VQ for memory balloon device, and registers
memory failure notifier list. During the guest kernel handles memory
failure, balloon device gets notified by memory failure notifier list,
and tells the host to recover the corrupted PFN(GPA) by the new VQ.


Most probably you might want to do that asynchronously, and once the
callback succeeds, un-poison the page.


Yes!



5, host side remaps the corrupted page(HVA), and tells the guest side to
unpoison the PFN(GPA). Then the guest fixes the corrupted page(GPA)
dynamically.


I think QEMU already does that during reboots. Now it would be triggered
by the guest for individual pages.

Yes, currently QEMU supports to un-poison corrupted pages during 
reset/reboot. We can reuse some code to do the work in this case, this 
allows a VM to fix corrupted pages as soon as possible(also no need to 
reset/reboot).




Because [4] and [5] are related to balloon device, also CC Michael,
David and Jason.


Doesn't sound too crazy for me, although it's a shame that we always
have to use virtio-balloon for such fairly balloon-unrelated things.


Thanks!

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH 3/4] mm/memofy-failure.c: optimize hwpoison_filter

2022-05-06 Thread zhenwei pi




On 5/6/22 16:59, Naoya Horiguchi wrote:

On Fri, Apr 29, 2022 at 10:22:05PM +0800, zhenwei pi wrote:

In the memory failure procedure, hwpoison_filter has higher priority,
if memory_filter() filters the error event, there is no need to do
the further work.


Could you clarify what problem you are trying to solve (what does
"optimize" mean in this context or what is the benefit)?



OK. The background of this work:
As well known, the memory failure mechanism handles memory corrupted 
event, and try to send SIGBUS to the user process which uses this 
corrupted page.


For the virtualization case, QEMU catches SIGBUS and tries to inject MCE 
into the guest, and the guest handles memory failure again. Thus the 
guest gets the minimal effect from hardware memory corruption.


The further step I'm working on:
1, try to modify code to decrease poisoned pages in a single place 
(mm/memofy-failure.c: simplify num_poisoned_pages_dec in this series).


2, try to use page_handle_poison() to handle SetPageHWPoison() and 
num_poisoned_pages_inc() together. It would be best to call 
num_poisoned_pages_inc() in a single place too. I'm not sure if this is 
possible or not, please correct me if I misunderstand.


3, introduce memory failure notifier list in memory-failure.c: notify 
the corrupted PFN to someone who registers this list.
If I can complete [1] and [2] part, [3] will be quite easy(just call 
notifier list after increasing poisoned page).


4, introduce memory recover VQ for memory balloon device, and registers 
memory failure notifier list. During the guest kernel handles memory 
failure, balloon device gets notified by memory failure notifier list, 
and tells the host to recover the corrupted PFN(GPA) by the new VQ.


5, host side remaps the corrupted page(HVA), and tells the guest side to 
unpoison the PFN(GPA). Then the guest fixes the corrupted page(GPA) 
dynamically.


Because [4] and [5] are related to balloon device, also CC Michael, 
David and Jason.



Now hwpoison_filter() can be called both with *and* without taking page 
refcount.
It's mainly called *with* taking page refcount in order to make sure that the
actual handling process is executed only for pages that meet a given condition.
IOW, it's important to prevent pages which do not meet the condition from going
ahead to further steps (false-positive is not permitted).  So this type of
callsite should not be omittable.

As for the other case, hwpoison_filter() is also called in hwpoison_inject()
*without* taking page refcount.  This actually has a different nuance and
intended to speculatively filter the injection events before setting
PageHWPoison flag to reduce the noise due to setting PG_hwpoison temporary.
The point is that it's not intended here to filter precisely and this callsite
is omittable.

So in my understanding, we need keep hwpoison_filter() after taking page
refcount as we do now.  Maybe optionally and additionally calling
hwpoison_filter() at the beginning of memory_failure() might be possible,
but I'm not sure yet how helpful...

Thanks,
Naoya Horiguchi



Cc: Wu Fengguang 
Signed-off-by: zhenwei pi 
---
  mm/memory-failure.c | 14 +-
  1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index ece05858568f..a6a27c8b800f 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1800,6 +1800,11 @@ int memory_failure(unsigned long pfn, int flags)
goto unlock_mutex;
}
  
+	if (hwpoison_filter(p)) {

+   res = -EOPNOTSUPP;
+   goto unlock_mutex;
+   }
+
  try_again:
res = try_memory_failure_hugetlb(pfn, flags, );
if (hugetlb)
@@ -1937,15 +1942,6 @@ int memory_failure(unsigned long pfn, int flags)
 */
page_flags = p->flags;
  
-	if (hwpoison_filter(p)) {

-   if (TestClearPageHWPoison(p))
-   num_poisoned_pages_dec();
-   unlock_page(p);
-   put_page(p);
-   res = -EOPNOTSUPP;
-   goto unlock_mutex;
-   }
-
/*
 * __munlock_pagevec may clear a writeback page's LRU flag without
 * page_lock. We need wait writeback completion for this page or it
--
2.20.1



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v6 2/5] virtio-crypto: use private buffer for control request

2022-05-06 Thread zhenwei pi
Originally, all of the control requests share a single buffer(
ctrl & input & ctrl_status fields in struct virtio_crypto), this
allows queue depth 1 only, the performance of control queue gets
limited by this design.

In this patch, each request allocates request buffer dynamically, and
free buffer after request, so the scope protected by ctrl_lock also
get optimized here.
It's possible to optimize control queue depth in the next step.

A necessary comment is already in code, still describe it again:
/*
 * Note: there are padding fields in request, clear them to zero before
 * sending to host to avoid to divulge any information.
 * Ex, virtio_crypto_ctrl_request::ctrl::u::destroy_session::padding[48]
 */
So use kzalloc to allocate buffer of struct virtio_crypto_ctrl_request.

Potentially dereferencing uninitialized variables:
Reported-by: kernel test robot 
Reported-by: Dan Carpenter 

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Reviewed-by: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 57 ---
 drivers/crypto/virtio/virtio_crypto_common.h  | 17 --
 .../virtio/virtio_crypto_skcipher_algs.c  | 50 ++--
 3 files changed, 79 insertions(+), 45 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 20901a263fc8..698ea57e2649 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -108,16 +108,22 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_session_input *input;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
-   spin_lock(>ctrl_lock);
-   ctrl = >ctrl;
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req) {
+   err = -ENOMEM;
+   goto out;
+   }
+
+   ctrl = _ctrl_req->ctrl;
memcpy(>header, header, sizeof(ctrl->header));
memcpy(>u, para, sizeof(ctrl->u));
-   input = >input;
+   input = _ctrl_req->input;
input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
sg_init_one(_sg, ctrl, sizeof(*ctrl));
@@ -129,16 +135,22 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
+   spin_lock(>ctrl_lock);
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0)
+   if (err < 0) {
+   spin_unlock(>ctrl_lock);
goto out;
+   }
 
virtqueue_kick(vcrypto->ctrl_vq);
while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
+   spin_unlock(>ctrl_lock);
 
if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
+   pr_err("virtio_crypto: Create session failed status: %u\n",
+   le32_to_cpu(input->status));
err = -EINVAL;
goto out;
}
@@ -148,13 +160,9 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
err = 0;
 
 out:
-   spin_unlock(>ctrl_lock);
+   kfree(vc_ctrl_req);
kfree_sensitive(pkey);
 
-   if (err < 0)
-   pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(input->status));
-
return err;
 }
 
@@ -167,15 +175,18 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
int err;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_inhdr *ctrl_status;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
-   spin_lock(>ctrl_lock);
-   if (!ctx->session_valid) {
-   err = 0;
-   goto out;
-   }
-   ctrl_status = >ctrl_status;
+   if (!ctx->session_valid)
+   return 0;
+
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req)
+   return -ENOMEM;
+
+   ctrl_status = _ctrl_req->ctrl_status;
ctrl_status->status = VIRTIO_CRYPTO_ERR;
-   ctrl = >ctrl;
+   ctrl = _ctrl_req->ctrl;
ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
ctrl->header.queue_id = 0;
 
@@ -188,16 +199,22 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
sg_init_one(_sg, _status->status, 
sizeof(ctrl_status->status));

[PATCH v6 4/5] virtio-crypto: adjust dst_len at ops callback

2022-05-06 Thread zhenwei pi
From: lei he 

For some akcipher operations(eg, decryption of pkcs1pad(rsa)),
the length of returned result maybe less than akcipher_req->dst_len,
we need to recalculate the actual dst_len through the virt-queue
protocol.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Reviewed-by: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 382ccec9ab12..2a60d0525cde 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -90,9 +90,12 @@ static void virtio_crypto_dataq_akcipher_callback(struct 
virtio_crypto_request *
}
 
akcipher_req = vc_akcipher_req->akcipher_req;
-   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY)
+   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+   /* actuall length maybe less than dst buffer */
+   akcipher_req->dst_len = len - sizeof(vc_req->status);
sg_copy_from_buffer(akcipher_req->dst, 
sg_nents(akcipher_req->dst),
vc_akcipher_req->dst_buf, 
akcipher_req->dst_len);
+   }
virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, 
error);
 }
 
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v6 3/5] virtio-crypto: wait ctrl queue instead of busy polling

2022-05-06 Thread zhenwei pi
Originally, after submitting request into virtio crypto control
queue, the guest side polls the result from the virt queue. This
works like following:
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  busy poll virtqueue
   |
  spin_unlock(>ctrl_lock)
  ...

There are two problems:
1, The queue depth is always 1, the performance of a virtio crypto
   device gets limited. Multi user processes share a single control
   queue, and hit spin lock race from control queue. Test on Intel
   Platinum 8260, a single worker gets ~35K/s create/close session
   operations, and 8 workers get ~40K/s operations with 800% CPU
   utilization.
2, The control request is supposed to get handled immediately, but
   in the current implementation of QEMU(v6.2), the vCPU thread kicks
   another thread to do this work, the latency also gets unstable.
   Tracking latency of virtio_crypto_alg_akcipher_close_session in 5s:
usecs   : count distribution
 0 -> 1  : 0||
 2 -> 3  : 7||
 4 -> 7  : 72   ||
 8 -> 15 : 186485   ||
16 -> 31 : 687  ||
32 -> 63 : 5||
64 -> 127: 3||
   128 -> 255: 1||
   256 -> 511: 0||
   512 -> 1023   : 0||
  1024 -> 2047   : 0||
  2048 -> 4095   : 0||
  4096 -> 8191   : 0||
  8192 -> 16383  : 2||
This means that a CPU may hold vcrypto->ctrl_lock as long as 8192~16383us.

To improve the performance of control queue, a request on control queue
waits completion instead of busy polling to reduce lock racing, and gets
completed by control queue callback.
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  -spin_unlock(>ctrl_lock)--
 /  /  \ \
 |  |  | |
wait   wait   wait  wait

Test this patch, the guest side get ~200K/s operations with 300% CPU
utilization.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Reviewed-by: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 29 ++-
 drivers/crypto/virtio/virtio_crypto_common.h  |  4 ++
 drivers/crypto/virtio/virtio_crypto_core.c| 52 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 34 ++--
 4 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 698ea57e2649..382ccec9ab12 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -103,7 +103,6 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
struct scatterlist outhdr_sg, key_sg, inhdr_sg, *sgs[3];
struct virtio_crypto *vcrypto = ctx->vcrypto;
uint8_t *pkey;
-   unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
@@ -135,18 +134,9 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
-   spin_lock(>ctrl_lock);
-   err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0) {
-   spin_unlock(>ctrl_lock);
+   err = virtio_crypto_ctrl_vq_request(vcrypto, sgs, num_out, num_in, 
vc_ctrl_req);
+   if (err < 0)
goto out;
-   }
-
-   virtqueue_kick(vcrypto->ctrl_vq);
-   while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
-  !virtqueue_is_broken(vcrypto->ctrl_vq))
-   cpu_relax();
-   spin_unlock(>ctrl_lock);
 
if (le32_to_cpu(input-

[PATCH v6 5/5] virtio-crypto: enable retry for virtio-crypto-dev

2022-05-06 Thread zhenwei pi
From: lei he 

Enable retry for virtio-crypto-dev, so that crypto-engine
can process cipher-requests parallelly.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Reviewed-by: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_core.c 
b/drivers/crypto/virtio/virtio_crypto_core.c
index 60490ffa3df1..1198bd306365 100644
--- a/drivers/crypto/virtio/virtio_crypto_core.c
+++ b/drivers/crypto/virtio/virtio_crypto_core.c
@@ -144,7 +144,8 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi)
spin_lock_init(>data_vq[i].lock);
vi->data_vq[i].vq = vqs[i];
/* Initialize crypto engine */
-   vi->data_vq[i].engine = crypto_engine_alloc_init(dev, 1);
+   vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, 
true, NULL, true,
+   
virtqueue_get_vring_size(vqs[i]));
if (!vi->data_vq[i].engine) {
ret = -ENOMEM;
goto err_engine;
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v6 1/5] virtio-crypto: change code style

2022-05-06 Thread zhenwei pi
Use temporary variable to make code easy to read and maintain.
/* Pad cipher's parameters */
vcrypto->ctrl.u.sym_create_session.op_type =
cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.algo =
vcrypto->ctrl.header.algo;
vcrypto->ctrl.u.sym_create_session.u.cipher.para.keylen =
cpu_to_le32(keylen);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.op =
cpu_to_le32(op);
-->
sym_create_session = >u.sym_create_session;
sym_create_session->op_type = cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
sym_create_session->u.cipher.para.algo = ctrl->header.algo;
sym_create_session->u.cipher.para.keylen = cpu_to_le32(keylen);
sym_create_session->u.cipher.para.op = cpu_to_le32(op);

The new style shows more obviously:
- the variable we want to operate.
- an assignment statement in a single line.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Reviewed-by: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 40 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 72 +--
 2 files changed, 59 insertions(+), 53 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index f3ec9420215e..20901a263fc8 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -106,23 +106,27 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_session_input *input;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
spin_lock(>ctrl_lock);
-   memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-   memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-   vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+   ctrl = >ctrl;
+   memcpy(>header, header, sizeof(ctrl->header));
+   memcpy(>u, para, sizeof(ctrl->u));
+   input = >input;
+   input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_init_one(_sg, ctrl, sizeof(*ctrl));
sgs[num_out++] = _sg;
 
sg_init_one(_sg, pkey, keylen);
sgs[num_out++] = _sg;
 
-   sg_init_one(_sg, >input, sizeof(vcrypto->input));
+   sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
@@ -134,12 +138,12 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
 
-   if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+   if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
err = -EINVAL;
goto out;
}
 
-   ctx->session_id = le64_to_cpu(vcrypto->input.session_id);
+   ctx->session_id = le64_to_cpu(input->session_id);
ctx->session_valid = true;
err = 0;
 
@@ -149,7 +153,7 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
 
if (err < 0)
pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(vcrypto->input.status));
+   le32_to_cpu(input->status));
 
return err;
 }
@@ -161,23 +165,27 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
struct virtio_crypto *vcrypto = ctx->vcrypto;
unsigned int num_out = 0, num_in = 0, inlen;
int err;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_inhdr *ctrl_status;
 
spin_lock(>ctrl_lock);
if (!ctx->session_valid) {
err = 0;
goto out;
}
-   vcrypto->ctrl_status.status = VIRTIO_CRYPTO_ERR;
-   vcrypto->ctrl.header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
-   vcrypto->ctrl.header.queue_id = 0;
+   ctrl_status = >ctrl_status;
+   ctrl_status->status = VIRTIO_CRYPTO_ERR;
+   ctrl = >ctrl;
+   ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
+   ctrl->header.queue_id = 0;
 
-   destroy_session = >ctrl.u.destroy_session;
+   destroy_session = >u.destroy_session;
destroy_session->session_id = cpu_to_le64(ctx->session_id);
 
-   sg_init_one(_sg, >ctrl, siz

[PATCH v6 0/5] virtio-crypto: Improve performance

2022-05-06 Thread zhenwei pi
v5 -> v6:
 - Minor fix for crypto_engine_alloc_init_and_set().
 - All the patches have been reviewed by Gonglei, add this in patch.
 Thanks to Gonglei.

v4 -> v5:
 - Fix potentially dereferencing uninitialized variables in
   'virtio-crypto: use private buffer for control request'.
   Thanks to Dan Carpenter!

v3 -> v4:
 - Don't create new file virtio_common.c, the new functions are added
   into virtio_crypto_core.c
 - Split the first patch into two parts:
 1, change code style,
 2, use private buffer instead of shared buffer
 - Remove relevant change.
 - Other minor changes.

v2 -> v3:
 - Jason suggested that spliting the first patch into two part:
 1, using private buffer
 2, remove the busy polling
   Rework as Jason's suggestion, this makes the smaller change in
   each one and clear.

v1 -> v2:
 - Use kfree instead of kfree_sensitive for insensitive buffer.
 - Several coding style fix.
 - Use memory from current node, instead of memory close to device
 - Add more message in commit, also explain why removing per-device
   request buffer.
 - Add necessary comment in code to explain why using kzalloc to
   allocate struct virtio_crypto_ctrl_request.

v1:
The main point of this series is to improve the performance for
virtio crypto:
- Use wait mechanism instead of busy polling for ctrl queue, this
  reduces CPU and lock racing, it's possiable to create/destroy session
  parallelly, QPS increases from ~40K/s to ~200K/s.
- Enable retry on crypto engine to improve performance for data queue,
  this allows the larger depth instead of 1.
- Fix dst data length in akcipher service.
- Other style fix.

lei he (2):
  virtio-crypto: adjust dst_len at ops callback
  virtio-crypto: enable retry for virtio-crypto-dev

zhenwei pi (3):
  virtio-crypto: change code style
  virtio-crypto: use private buffer for control request
  virtio-crypto: wait ctrl queue instead of busy polling

 .../virtio/virtio_crypto_akcipher_algs.c  |  95 ++--
 drivers/crypto/virtio/virtio_crypto_common.h  |  21 ++-
 drivers/crypto/virtio/virtio_crypto_core.c|  55 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 140 --
 4 files changed, 182 insertions(+), 129 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: RE: [PATCH v5 5/5] virtio-crypto: enable retry for virtio-crypto-dev

2022-05-06 Thread zhenwei pi

On 5/6/22 17:34, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Thursday, May 5, 2022 5:24 PM
To: Gonglei (Arei) ; m...@redhat.com
Cc: jasow...@redhat.com; herb...@gondor.apana.org.au;
linux-ker...@vger.kernel.org; virtualization@lists.linux-foundation.org;
linux-cry...@vger.kernel.org; helei.si...@bytedance.com;
pizhen...@bytedance.com; da...@davemloft.net
Subject: [PATCH v5 5/5] virtio-crypto: enable retry for virtio-crypto-dev

From: lei he 

Enable retry for virtio-crypto-dev, so that crypto-engine can process
cipher-requests parallelly.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
  drivers/crypto/virtio/virtio_crypto_core.c | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_core.c
b/drivers/crypto/virtio/virtio_crypto_core.c
index 60490ffa3df1..f67e0d4c1b0c 100644
--- a/drivers/crypto/virtio/virtio_crypto_core.c
+++ b/drivers/crypto/virtio/virtio_crypto_core.c
@@ -144,7 +144,8 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi)
spin_lock_init(>data_vq[i].lock);
vi->data_vq[i].vq = vqs[i];
/* Initialize crypto engine */
-   vi->data_vq[i].engine = crypto_engine_alloc_init(dev, 1);
+   vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, 
true,
NULL, 1,
+   
virtqueue_get_vring_size(vqs[i]));


Here the '1' can be 'true' too.

Sure, you can add

Reviewed-by: Gonglei 

Regards,
-Gonglei


if (!vi->data_vq[i].engine) {
ret = -ENOMEM;
goto err_engine;
--
2.20.1




Thanks to Lei!

Hi, Michael
I would appreciate it if you could apply this minor change, or I send 
the v6 series, which one do you prefer?


--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: PING: [PATCH v4 0/5] virtio-crypto: Improve performance

2022-05-05 Thread zhenwei pi



On 5/5/22 12:57, Michael S. Tsirkin wrote:

On Thu, May 05, 2022 at 03:14:40AM +, Gonglei (Arei) wrote:




-Original Message-
From: zhenwei pi [mailto:pizhen...@bytedance.com]
Sent: Thursday, May 5, 2022 10:35 AM
To: Gonglei (Arei) ; m...@redhat.com;
jasow...@redhat.com
Cc: herb...@gondor.apana.org.au; linux-ker...@vger.kernel.org;
virtualization@lists.linux-foundation.org; linux-cry...@vger.kernel.org;
helei.si...@bytedance.com; da...@davemloft.net
Subject: PING: [PATCH v4 0/5] virtio-crypto: Improve performance

Hi, Lei

Jason replied in another patch:
Still hundreds of lines of changes, I'd leave this change to other maintainers 
to
decide.

Quite frankly, the virtio crypto driver changed only a few in the past, and the
performance of control queue is not good enough. I am in doubt about that this
driver is not used widely. So I'd like to rework a lot, it would be best to 
complete
this work in 5.18 window.

This gets different point with Jason. I would appreciate it if you could give me
any hint.



This is already in my todo list.

Regards,
-Gonglei


It's been out a month though, not really acceptable latency for review.
So I would apply this for next,  but you need to address Dan Captenter's
comment, and look for simular patterns elesewhere in your patch.



I fixed this in the v5 series. Thanks!

--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v5 5/5] virtio-crypto: enable retry for virtio-crypto-dev

2022-05-05 Thread zhenwei pi
From: lei he 

Enable retry for virtio-crypto-dev, so that crypto-engine
can process cipher-requests parallelly.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_core.c 
b/drivers/crypto/virtio/virtio_crypto_core.c
index 60490ffa3df1..f67e0d4c1b0c 100644
--- a/drivers/crypto/virtio/virtio_crypto_core.c
+++ b/drivers/crypto/virtio/virtio_crypto_core.c
@@ -144,7 +144,8 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi)
spin_lock_init(>data_vq[i].lock);
vi->data_vq[i].vq = vqs[i];
/* Initialize crypto engine */
-   vi->data_vq[i].engine = crypto_engine_alloc_init(dev, 1);
+   vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, 
true, NULL, 1,
+   
virtqueue_get_vring_size(vqs[i]));
if (!vi->data_vq[i].engine) {
ret = -ENOMEM;
goto err_engine;
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v5 4/5] virtio-crypto: adjust dst_len at ops callback

2022-05-05 Thread zhenwei pi
From: lei he 

For some akcipher operations(eg, decryption of pkcs1pad(rsa)),
the length of returned result maybe less than akcipher_req->dst_len,
we need to recalculate the actual dst_len through the virt-queue
protocol.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 382ccec9ab12..2a60d0525cde 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -90,9 +90,12 @@ static void virtio_crypto_dataq_akcipher_callback(struct 
virtio_crypto_request *
}
 
akcipher_req = vc_akcipher_req->akcipher_req;
-   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY)
+   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+   /* actuall length maybe less than dst buffer */
+   akcipher_req->dst_len = len - sizeof(vc_req->status);
sg_copy_from_buffer(akcipher_req->dst, 
sg_nents(akcipher_req->dst),
vc_akcipher_req->dst_buf, 
akcipher_req->dst_len);
+   }
virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, 
error);
 }
 
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v5 3/5] virtio-crypto: wait ctrl queue instead of busy polling

2022-05-05 Thread zhenwei pi
Originally, after submitting request into virtio crypto control
queue, the guest side polls the result from the virt queue. This
works like following:
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  busy poll virtqueue
   |
  spin_unlock(>ctrl_lock)
  ...

There are two problems:
1, The queue depth is always 1, the performance of a virtio crypto
   device gets limited. Multi user processes share a single control
   queue, and hit spin lock race from control queue. Test on Intel
   Platinum 8260, a single worker gets ~35K/s create/close session
   operations, and 8 workers get ~40K/s operations with 800% CPU
   utilization.
2, The control request is supposed to get handled immediately, but
   in the current implementation of QEMU(v6.2), the vCPU thread kicks
   another thread to do this work, the latency also gets unstable.
   Tracking latency of virtio_crypto_alg_akcipher_close_session in 5s:
usecs   : count distribution
 0 -> 1  : 0||
 2 -> 3  : 7||
 4 -> 7  : 72   ||
 8 -> 15 : 186485   ||
16 -> 31 : 687  ||
32 -> 63 : 5||
64 -> 127: 3||
   128 -> 255: 1||
   256 -> 511: 0||
   512 -> 1023   : 0||
  1024 -> 2047   : 0||
  2048 -> 4095   : 0||
  4096 -> 8191   : 0||
  8192 -> 16383  : 2||
This means that a CPU may hold vcrypto->ctrl_lock as long as 8192~16383us.

To improve the performance of control queue, a request on control queue
waits completion instead of busy polling to reduce lock racing, and gets
completed by control queue callback.
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  -spin_unlock(>ctrl_lock)--
 /  /  \ \
 |  |  | |
wait   wait   wait  wait

Test this patch, the guest side get ~200K/s operations with 300% CPU
utilization.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 29 ++-
 drivers/crypto/virtio/virtio_crypto_common.h  |  4 ++
 drivers/crypto/virtio/virtio_crypto_core.c| 52 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 34 ++--
 4 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 698ea57e2649..382ccec9ab12 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -103,7 +103,6 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
struct scatterlist outhdr_sg, key_sg, inhdr_sg, *sgs[3];
struct virtio_crypto *vcrypto = ctx->vcrypto;
uint8_t *pkey;
-   unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
@@ -135,18 +134,9 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
-   spin_lock(>ctrl_lock);
-   err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0) {
-   spin_unlock(>ctrl_lock);
+   err = virtio_crypto_ctrl_vq_request(vcrypto, sgs, num_out, num_in, 
vc_ctrl_req);
+   if (err < 0)
goto out;
-   }
-
-   virtqueue_kick(vcrypto->ctrl_vq);
-   while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
-  !virtqueue_is_broken(vcrypto->ctrl_vq))
-   cpu_relax();
-   spin_unlock(>ctrl_lock);
 
if (le32_to_cpu(input->status) != VIRTIO

[PATCH v5 2/5] virtio-crypto: use private buffer for control request

2022-05-05 Thread zhenwei pi
Originally, all of the control requests share a single buffer(
ctrl & input & ctrl_status fields in struct virtio_crypto), this
allows queue depth 1 only, the performance of control queue gets
limited by this design.

In this patch, each request allocates request buffer dynamically, and
free buffer after request, so the scope protected by ctrl_lock also
get optimized here.
It's possible to optimize control queue depth in the next step.

A necessary comment is already in code, still describe it again:
/*
 * Note: there are padding fields in request, clear them to zero before
 * sending to host to avoid to divulge any information.
 * Ex, virtio_crypto_ctrl_request::ctrl::u::destroy_session::padding[48]
 */
So use kzalloc to allocate buffer of struct virtio_crypto_ctrl_request.

Potentially dereferencing uninitialized variables:
Reported-by: kernel test robot 
Reported-by: Dan Carpenter 

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 57 ---
 drivers/crypto/virtio/virtio_crypto_common.h  | 17 --
 .../virtio/virtio_crypto_skcipher_algs.c  | 50 ++--
 3 files changed, 79 insertions(+), 45 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 20901a263fc8..698ea57e2649 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -108,16 +108,22 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_session_input *input;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
-   spin_lock(>ctrl_lock);
-   ctrl = >ctrl;
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req) {
+   err = -ENOMEM;
+   goto out;
+   }
+
+   ctrl = _ctrl_req->ctrl;
memcpy(>header, header, sizeof(ctrl->header));
memcpy(>u, para, sizeof(ctrl->u));
-   input = >input;
+   input = _ctrl_req->input;
input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
sg_init_one(_sg, ctrl, sizeof(*ctrl));
@@ -129,16 +135,22 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
+   spin_lock(>ctrl_lock);
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0)
+   if (err < 0) {
+   spin_unlock(>ctrl_lock);
goto out;
+   }
 
virtqueue_kick(vcrypto->ctrl_vq);
while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
+   spin_unlock(>ctrl_lock);
 
if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
+   pr_err("virtio_crypto: Create session failed status: %u\n",
+   le32_to_cpu(input->status));
err = -EINVAL;
goto out;
}
@@ -148,13 +160,9 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
err = 0;
 
 out:
-   spin_unlock(>ctrl_lock);
+   kfree(vc_ctrl_req);
kfree_sensitive(pkey);
 
-   if (err < 0)
-   pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(input->status));
-
return err;
 }
 
@@ -167,15 +175,18 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
int err;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_inhdr *ctrl_status;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
-   spin_lock(>ctrl_lock);
-   if (!ctx->session_valid) {
-   err = 0;
-   goto out;
-   }
-   ctrl_status = >ctrl_status;
+   if (!ctx->session_valid)
+   return 0;
+
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req)
+   return -ENOMEM;
+
+   ctrl_status = _ctrl_req->ctrl_status;
ctrl_status->status = VIRTIO_CRYPTO_ERR;
-   ctrl = >ctrl;
+   ctrl = _ctrl_req->ctrl;
ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
ctrl->header.queue_id = 0;
 
@@ -188,16 +199,22 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
sg_init_one(_sg, _status->status, 
sizeof(ctrl_status->status));
sgs[num_out 

[PATCH v5 1/5] virtio-crypto: change code style

2022-05-05 Thread zhenwei pi
Use temporary variable to make code easy to read and maintain.
/* Pad cipher's parameters */
vcrypto->ctrl.u.sym_create_session.op_type =
cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.algo =
vcrypto->ctrl.header.algo;
vcrypto->ctrl.u.sym_create_session.u.cipher.para.keylen =
cpu_to_le32(keylen);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.op =
cpu_to_le32(op);
-->
sym_create_session = >u.sym_create_session;
sym_create_session->op_type = cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
sym_create_session->u.cipher.para.algo = ctrl->header.algo;
sym_create_session->u.cipher.para.keylen = cpu_to_le32(keylen);
sym_create_session->u.cipher.para.op = cpu_to_le32(op);

The new style shows more obviously:
- the variable we want to operate.
- an assignment statement in a single line.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 40 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 72 +--
 2 files changed, 59 insertions(+), 53 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index f3ec9420215e..20901a263fc8 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -106,23 +106,27 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_session_input *input;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
spin_lock(>ctrl_lock);
-   memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-   memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-   vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+   ctrl = >ctrl;
+   memcpy(>header, header, sizeof(ctrl->header));
+   memcpy(>u, para, sizeof(ctrl->u));
+   input = >input;
+   input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_init_one(_sg, ctrl, sizeof(*ctrl));
sgs[num_out++] = _sg;
 
sg_init_one(_sg, pkey, keylen);
sgs[num_out++] = _sg;
 
-   sg_init_one(_sg, >input, sizeof(vcrypto->input));
+   sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
@@ -134,12 +138,12 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
 
-   if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+   if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
err = -EINVAL;
goto out;
}
 
-   ctx->session_id = le64_to_cpu(vcrypto->input.session_id);
+   ctx->session_id = le64_to_cpu(input->session_id);
ctx->session_valid = true;
err = 0;
 
@@ -149,7 +153,7 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
 
if (err < 0)
pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(vcrypto->input.status));
+   le32_to_cpu(input->status));
 
return err;
 }
@@ -161,23 +165,27 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
struct virtio_crypto *vcrypto = ctx->vcrypto;
unsigned int num_out = 0, num_in = 0, inlen;
int err;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_inhdr *ctrl_status;
 
spin_lock(>ctrl_lock);
if (!ctx->session_valid) {
err = 0;
goto out;
}
-   vcrypto->ctrl_status.status = VIRTIO_CRYPTO_ERR;
-   vcrypto->ctrl.header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
-   vcrypto->ctrl.header.queue_id = 0;
+   ctrl_status = >ctrl_status;
+   ctrl_status->status = VIRTIO_CRYPTO_ERR;
+   ctrl = >ctrl;
+   ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
+   ctrl->header.queue_id = 0;
 
-   destroy_session = >ctrl.u.destroy_session;
+   destroy_session = >u.destroy_session;
destroy_session->session_id = cpu_to_le64(ctx->session_id);
 
-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_i

[PATCH v5 0/5] virtio-crypto: Improve performance

2022-05-05 Thread zhenwei pi
v4 -> v5:
 - Fix potentially dereferencing uninitialized variables in
   'virtio-crypto: use private buffer for control request'.
   Thanks to Dan Carpenter!

v3 -> v4:
 - Don't create new file virtio_common.c, the new functions are added
   into virtio_crypto_core.c
 - Split the first patch into two parts:
 1, change code style,
 2, use private buffer instead of shared buffer
 - Remove relevant change.
 - Other minor changes.

v2 -> v3:
 - Jason suggested that spliting the first patch into two part:
 1, using private buffer
 2, remove the busy polling
   Rework as Jason's suggestion, this makes the smaller change in
   each one and clear.

v1 -> v2:
 - Use kfree instead of kfree_sensitive for insensitive buffer.
 - Several coding style fix.
 - Use memory from current node, instead of memory close to device
 - Add more message in commit, also explain why removing per-device
   request buffer.
 - Add necessary comment in code to explain why using kzalloc to
   allocate struct virtio_crypto_ctrl_request.

v1:
The main point of this series is to improve the performance for
virtio crypto:
- Use wait mechanism instead of busy polling for ctrl queue, this
  reduces CPU and lock racing, it's possiable to create/destroy session
  parallelly, QPS increases from ~40K/s to ~200K/s.
- Enable retry on crypto engine to improve performance for data queue,
  this allows the larger depth instead of 1.
- Fix dst data length in akcipher service.
- Other style fix.

lei he (2):
  virtio-crypto: adjust dst_len at ops callback
  virtio-crypto: enable retry for virtio-crypto-dev

zhenwei pi (3):
  virtio-crypto: change code style
  virtio-crypto: use private buffer for control request
  virtio-crypto: wait ctrl queue instead of busy polling

 .../virtio/virtio_crypto_akcipher_algs.c  |  95 ++--
 drivers/crypto/virtio/virtio_crypto_common.h  |  21 ++-
 drivers/crypto/virtio/virtio_crypto_core.c|  55 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 140 --
 4 files changed, 182 insertions(+), 129 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


PING: [PATCH v4 0/5] virtio-crypto: Improve performance

2022-05-04 Thread zhenwei pi

Hi, Lei

Jason replied in another patch:
Still hundreds of lines of changes, I'd leave this change to other
maintainers to decide.

Quite frankly, the virtio crypto driver changed only a few in the past, 
and the performance of control queue is not good enough. I am in doubt 
about that this driver is not used widely. So I'd like to rework a lot, 
it would be best to complete this work in 5.18 window.


This gets different point with Jason. I would appreciate it if you could 
give me any hint.


On 4/24/22 18:41, zhenwei pi wrote:

Hi, Lei
I'd like to move helper and callback functions(Eg, virtcrypto_clear_request
  and virtcrypto_ctrlq_callback) from xx_core.c to xx_common.c,
then the xx_core.c supports:
   - probe/remove/irq affinity seting for a virtio device
   - basic virtio related operations

xx_common.c supports:
   - common helpers/functions for algos

Do you have any suggestion about this?

v3 -> v4:
  - Don't create new file virtio_common.c, the new functions are added
into virtio_crypto_core.c
  - Split the first patch into two parts:
  1, change code style,
  2, use private buffer instead of shared buffer
  - Remove relevant change.
  - Other minor changes.

v2 -> v3:
  - Jason suggested that spliting the first patch into two part:
  1, using private buffer
  2, remove the busy polling
Rework as Jason's suggestion, this makes the smaller change in
each one and clear.

v1 -> v2:
  - Use kfree instead of kfree_sensitive for insensitive buffer.
  - Several coding style fix.
  - Use memory from current node, instead of memory close to device
  - Add more message in commit, also explain why removing per-device
request buffer.
  - Add necessary comment in code to explain why using kzalloc to
allocate struct virtio_crypto_ctrl_request.

v1:
The main point of this series is to improve the performance for
virtio crypto:
- Use wait mechanism instead of busy polling for ctrl queue, this
   reduces CPU and lock racing, it's possiable to create/destroy session
   parallelly, QPS increases from ~40K/s to ~200K/s.
- Enable retry on crypto engine to improve performance for data queue,
   this allows the larger depth instead of 1.
- Fix dst data length in akcipher service.
- Other style fix.

lei he (2):
   virtio-crypto: adjust dst_len at ops callback
   virtio-crypto: enable retry for virtio-crypto-dev

zhenwei pi (3):
   virtio-crypto: change code style
   virtio-crypto: use private buffer for control request
   virtio-crypto: wait ctrl queue instead of busy polling

  .../virtio/virtio_crypto_akcipher_algs.c  |  83 ++-
  drivers/crypto/virtio/virtio_crypto_common.h  |  21 ++-
  drivers/crypto/virtio/virtio_crypto_core.c|  55 ++-
  .../virtio/virtio_crypto_skcipher_algs.c  | 140 --
  4 files changed, 180 insertions(+), 119 deletions(-)



--
zhenwei pi
___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v5 9/9] crypto: Introduce RSA algorithm

2022-04-28 Thread zhenwei pi
There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj 
"/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=q...@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Signed-off-by: zhenwei pi 
Signed-off-by: lei he conf.crypto_services =
  1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
 backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
 backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
 /*
  * Set the Maximum length of crypto request.
  * Why this value? Just avoid to overflow when
  * memory allocation for each crypto request.
  */
-backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
 backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
 backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,53 @@ err:
return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+int virtio_rsa_hash, Error **errp)
+{
+switch (virtio_rsa_hash) {
+case VIRTIO_CRYPTO_RSA_MD5:
+return QCRYPTO_HASH_ALG_MD5;
+
+case VIRTIO_CRYPTO_RSA_SHA1:
+return QCRYPTO_HASH_ALG_SHA1;
+
+case VIRTIO_CRYPTO_RSA_SHA256:
+return QCRYPTO_HASH_ALG_SHA256;
+
+case VIRTIO_CRYPTO_RSA_SHA512:
+return QCRYPTO_HASH_ALG_SHA512;
+
+default:
+error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+return -1;
+}
+}
+
+static int cryptodev_builtin_set_rsa_options(
+int virtio_padding_algo,
+int virtio_hash_algo,
+QCryptoAkCipherOptionsRSA *opt,
+Error **errp)
+{
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+opt->hash_alg =
+cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
+if (opt->hash_alg < 0) {
+return -1;
+}
+return 0;
+}
+
+if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+return 0;
+}
+
+error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
+return -1;
+}
+
 static int cryptodev_builtin_create_cipher_session(
 

[PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher

2022-04-28 Thread zhenwei pi
From: Lei He 

Add unit test and benchmark test for crypto akcipher.

Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
Reviewed-by: Daniel P. Berrangé 
---
 tests/bench/benchmark-crypto-akcipher.c | 157 ++
 tests/bench/meson.build |   4 +
 tests/bench/test_akcipher_keys.inc  | 537 ++
 tests/unit/meson.build  |   1 +
 tests/unit/test-crypto-akcipher.c   | 711 
 5 files changed, 1410 insertions(+)
 create mode 100644 tests/bench/benchmark-crypto-akcipher.c
 create mode 100644 tests/bench/test_akcipher_keys.inc
 create mode 100644 tests/unit/test-crypto-akcipher.c

diff --git a/tests/bench/benchmark-crypto-akcipher.c 
b/tests/bench/benchmark-crypto-akcipher.c
new file mode 100644
index 00..c6c80c0be1
--- /dev/null
+++ b/tests/bench/benchmark-crypto-akcipher.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU Crypto akcipher speed benchmark
+ *
+ * Copyright (c) 2022 Bytedance
+ *
+ * Authors:
+ *lei he 
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/init.h"
+#include "crypto/akcipher.h"
+#include "standard-headers/linux/virtio_crypto.h"
+
+#include "test_akcipher_keys.inc"
+
+static bool keep_running;
+
+static void alarm_handler(int sig)
+{
+keep_running = false;
+}
+
+static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key,
+size_t keylen,
+QCryptoRSAPaddingAlgorithm padding,
+QCryptoHashAlgorithm hash)
+{
+QCryptoAkCipherOptions opt;
+QCryptoAkCipher *rsa;
+
+opt.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+opt.u.rsa.padding_alg = padding;
+opt.u.rsa.hash_alg = hash;
+rsa = qcrypto_akcipher_new(, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+   priv_key, keylen, _abort);
+return rsa;
+}
+
+static void test_rsa_speed(const uint8_t *priv_key, size_t keylen,
+   size_t key_size)
+{
+#define BYTE 8
+#define SHA1_DGST_LEN 20
+#define DURATION_SECONDS 10
+#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1
+#define HASH QCRYPTO_HASH_ALG_SHA1
+
+g_autoptr(QCryptoAkCipher) rsa =
+create_rsa_akcipher(priv_key, keylen, PADDING, HASH);
+g_autofree uint8_t *dgst = NULL;
+g_autofree uint8_t *signature = NULL;
+size_t count;
+
+dgst = g_new0(uint8_t, SHA1_DGST_LEN);
+memset(dgst, g_test_rand_int(), SHA1_DGST_LEN);
+signature = g_new0(uint8_t, key_size / BYTE);
+
+g_test_message("benchmark rsa%lu (%s-%s) sign in %d seconds", key_size,
+   QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   DURATION_SECONDS);
+alarm(DURATION_SECONDS);
+g_test_timer_start();
+for (keep_running = true, count = 0; keep_running; ++count) {
+g_assert(qcrypto_akcipher_sign(rsa, dgst, SHA1_DGST_LEN,
+   signature, key_size / BYTE,
+   _abort) > 0);
+}
+g_test_timer_elapsed();
+g_test_message("rsa%lu (%s-%s) sign %lu times in %.2f seconds,"
+   " %.2f times/sec ",
+   key_size,  QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   count, g_test_timer_last(),
+   (double)count / g_test_timer_last());
+
+g_test_message("benchmark rsa%lu (%s-%s) verify in %d seconds", key_size,
+   QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   DURATION_SECONDS);
+alarm(DURATION_SECONDS);
+g_test_timer_start();
+for (keep_running = true, count = 0; keep_running; ++count) {
+g_assert(qcrypto_akcipher_verify(rsa, signature, key_size / BYTE,
+ dgst, SHA1_DGST_LEN,
+ _abort) == 0);
+}
+g_test_timer_elapsed();
+g_test_message("rsa%lu (%s-%s) verify %lu times in %.2f seconds,"
+   " %.2f times/sec ",
+   key_size, QCryptoRSAPaddingAlgorithm_str(PADDING),
+   QCryptoHashAlgorithm_str(HASH),
+   count, g_test_timer_last(),
+   (double)count / g_test_timer_last());
+}
+
+static void test_rsa_1024_speed(const void *opaque)
+{
+size_t key_size = (size_t)opaque;
+test_rsa_speed(rsa1024_priv_key, sizeof(rsa1024_priv_key), key_size);
+}
+
+static void test_rsa_2048_speed(const void *opaque)
+{
+size_t key_size = (size_t)opaque;
+test_rsa_speed(rsa2048_priv_key, sizeof(rsa2048_priv_key), key_size);
+}
+
+

[PATCH v5 8/9] tests/crypto: Add test suite for RSA keys

2022-04-28 Thread zhenwei pi
From: Lei He 

As Daniel suggested, Add tests suite for rsakey, as a way to prove
that we can handle DER errors correctly.

Signed-off-by: lei he 
---
 tests/unit/test-crypto-akcipher.c | 285 +-
 1 file changed, 282 insertions(+), 3 deletions(-)

diff --git a/tests/unit/test-crypto-akcipher.c 
b/tests/unit/test-crypto-akcipher.c
index b5be563884..4f1f4214dd 100644
--- a/tests/unit/test-crypto-akcipher.c
+++ b/tests/unit/test-crypto-akcipher.c
@@ -517,6 +517,158 @@ static const uint8_t exp_ciphertext_rsa2048_pkcs1[] = {
 0xd0, 0x28, 0x03, 0x19, 0xa6, 0x06, 0x13, 0x45,
 };
 
+static const uint8_t rsa_private_key_lack_element[] = {
+/* RSAPrivateKey, offset: 0, length: 176 */
+0x30, 0x81, 0xb0,
+/* version, offset: 4, length: 1 */
+0x02, 0x01, 0x00,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* e, offset: 74, length: 3 */
+0x02, 0x03, 0x01, 0x00, 0x01,
+/* d, offset: 79, length: 64 */
+0x02, 0x40,
+0x1e, 0x80, 0xfe, 0xda, 0x65, 0xdb, 0x70, 0xb8,
+0x61, 0x91, 0x28, 0xbf, 0x6c, 0x32, 0xc1, 0x05,
+0xd1, 0x26, 0x6a, 0x1c, 0x83, 0xcc, 0xf4, 0x1f,
+0x53, 0x42, 0x72, 0x1f, 0x62, 0x57, 0x0a, 0xc4,
+0x66, 0x76, 0x30, 0x87, 0xb9, 0xb1, 0xb9, 0x6a,
+0x63, 0xfd, 0x8f, 0x3e, 0xfc, 0x35, 0x3f, 0xd6,
+0x2e, 0x6c, 0xc8, 0x70, 0x8a, 0x17, 0xc1, 0x28,
+0x6a, 0xfe, 0x51, 0x56, 0xb3, 0x92, 0x6f, 0x09,
+/* p, offset: 145, length: 33 */
+0x02, 0x21,
+0x00, 0xe3, 0x2e, 0x2d, 0x8d, 0xba, 0x1c, 0x34,
+0x4c, 0x49, 0x9f, 0xc1, 0xa6, 0xdd, 0xd7, 0x13,
+0x8d, 0x05, 0x48, 0xdd, 0xff, 0x5c, 0x30, 0xbc,
+0x6b, 0xc4, 0x18, 0x9d, 0xfc, 0xa2, 0xd0, 0x9b,
+0x4d,
+/* q, offset: 180, length: 33 */
+0x02, 0x21,
+0x00, 0xd1, 0x75, 0xaf, 0x4b, 0xc6, 0x1a, 0xb0,
+0x98, 0x14, 0x42, 0xae, 0x33, 0xf3, 0x44, 0xde,
+0x21, 0xcb, 0x04, 0xda, 0xfb, 0x1e, 0x35, 0x92,
+0xcd, 0x69, 0xc0, 0x83, 0x06, 0x83, 0x8e, 0x39,
+0x53,
+/* lack element: dp, dq, u */
+};
+
+static const uint8_t rsa_public_key_lack_element[] = {
+/* RSAPublicKey, offset: 0, length: 67 */
+0x30, 0x81, 0x43,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* lack element: e */
+};
+
+static const uint8_t rsa_public_key_empty_element[] = {
+/* RSAPublicKey, offset: 0, length: 69 */
+0x30, 0x81, 0x45,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* e: empty element */
+0x02, 0x00,
+};
+
+static const uint8_t rsa_private_key_empty_element[] = {
+/* RSAPrivateKey, offset: 0, length: 19 */
+0x30, 0x81, 0x13,
+/* version, offset: 4, length: 1 */
+0x02, 0x01, 0x00,
+/* n: empty element */
+0x02, 0x00,
+/* e: empty element */
+0x02, 0x00,
+/* d: empty element */
+0x02, 0x00,
+/* p: empty element */
+0x02, 0x00,
+/* q: empty element */
+0x02, 0x00,
+/* dp: empty element */
+0x02, 0x00,
+/* dq: empty element */
+0x02, 0x00,
+/* u: empty element */
+0x02, 0x00,
+};
+
+static const uint8_t rsa_public_key_invalid_length_val[] = {
+/* RSAPublicKey, INVALID length: 313 */
+0x30, 0x82, 0x01, 0x39,
+/* n, offset: 7, length: 65 */
+0x02, 0x41,
+0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+0xf7,
+/* e, */
+0x02, 0x03, 0x01, 0x00, 0x01,  /* INTEGER, 

[PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt

2022-04-28 Thread zhenwei pi
From: Lei He 

Added gcryt implementation of RSA algorithm, RSA algorithm
implemented by gcrypt has a higher priority than nettle because
it supports raw padding.

Signed-off-by: lei he 
---
 crypto/akcipher-gcrypt.c.inc | 520 +++
 crypto/akcipher.c|   4 +-
 2 files changed, 523 insertions(+), 1 deletion(-)
 create mode 100644 crypto/akcipher-gcrypt.c.inc

diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc
new file mode 100644
index 00..32ff502f71
--- /dev/null
+++ b/crypto/akcipher-gcrypt.c.inc
@@ -0,0 +1,520 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ */
+
+#include 
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoGcryptRSA {
+QCryptoAkCipher akcipher;
+gcry_sexp_t key;
+QCryptoRSAPaddingAlgorithm padding_alg;
+QCryptoHashAlgorithm hash_alg;
+} QCryptoGcryptRSA;
+
+static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher)
+{
+QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+if (!rsa) {
+return;
+}
+
+gcry_sexp_release(rsa->key);
+g_free(rsa);
+}
+
+static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
+const QCryptoAkCipherOptionsRSA *opt,
+QCryptoAkCipherKeyType type,
+const uint8_t *key,  size_t keylen,
+Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+switch (opts->alg) {
+case QCRYPTO_AKCIPHER_ALG_RSA:
+return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new(
+>u.rsa, type, key, keylen, errp);
+
+default:
+error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+return NULL;
+}
+
+return NULL;
+}
+
+static void qcrypto_gcrypt_set_rsa_size(QCryptoAkCipher *akcipher, gcry_mpi_t 
n)
+{
+size_t key_size = (gcry_mpi_get_nbits(n) + 7) / 8;
+akcipher->max_plaintext_len = key_size;
+akcipher->max_ciphertext_len = key_size;
+akcipher->max_dgst_len = key_size;
+akcipher->max_signature_len = key_size;
+}
+
+static int qcrypto_gcrypt_parse_rsa_private_key(
+QCryptoGcryptRSA *rsa,
+const uint8_t *key, size_t keylen)
+{
+g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen);
+gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
+bool compute_mul_inv = false;
+int ret = -1;
+gcry_error_t err;
+
+if (!rsa_key) {
+return ret;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->n.data, rsa_key->n.len, NULL);
+if (gcry_err_code(err) != 0) {
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->e.data, rsa_key->e.len, NULL);
+if (gcry_err_code(err) != 0) {
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->d.data, rsa_key->d.len, NULL);
+if (gcry_err_code(err) != 0) {
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->p.data, rsa_key->p.len, NULL);
+if (gcry_err_code(err) != 0) {
+goto cleanup;
+}
+
+err = gcry_mpi_scan(, GCRYMPI_FMT_STD,
+rsa_key->q.data, rsa_key->q.len, NULL);
+if (gcry_err_code(err) != 0) {
+goto cleanup;
+}
+
+if (gcry_mpi_cmp_ui(p, 0) > 0 && gcry_mpi_cmp_ui(q, 0) > 0) {
+compute_mul_inv = true;
+
+u = gcry_mpi_new(0);
+if (gcry_mpi_cmp(p, q) > 0) {
+gcry_mpi_swap(p, q);
+}
+gcry_mpi_invm(u, p, q);
+}
+
+if (compute_mul_inv) {
+err = gcry_sexp_build(>key, NULL,
+"(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))",
+n, e, d, p, q, u);
+} else {
+err = gcry_sexp_build(>key, NULL,
+"(private-key (rsa (n %m) (e %m) (d 

[PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed

2022-04-28 Thread zhenwei pi
From: Lei He 

Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
a 'real' RSA backend to handle request from guest side. It's
important to test RSA offload case without OS & hardware requirement.

Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 crypto/akcipher-nettle.c.inc | 432 +++
 crypto/akcipher.c|   4 +
 crypto/meson.build   |   4 +
 crypto/rsakey-builtin.c.inc  | 209 +
 crypto/rsakey-nettle.c.inc   | 154 +
 crypto/rsakey.c  |  44 
 crypto/rsakey.h  |  94 
 meson.build  |  11 +
 8 files changed, 952 insertions(+)
 create mode 100644 crypto/akcipher-nettle.c.inc
 create mode 100644 crypto/rsakey-builtin.c.inc
 create mode 100644 crypto/rsakey-nettle.c.inc
 create mode 100644 crypto/rsakey.c
 create mode 100644 crypto/rsakey.h

diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
new file mode 100644
index 00..1f688aa0f1
--- /dev/null
+++ b/crypto/akcipher-nettle.c.inc
@@ -0,0 +1,432 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include 
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoNettleRSA {
+QCryptoAkCipher akcipher;
+struct rsa_public_key pub;
+struct rsa_private_key priv;
+QCryptoRSAPaddingAlgorithm padding_alg;
+QCryptoHashAlgorithm hash_alg;
+} QCryptoNettleRSA;
+
+static void qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher)
+{
+QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+if (!rsa) {
+return;
+}
+
+rsa_public_key_clear(>pub);
+rsa_private_key_clear(>priv);
+g_free(rsa);
+}
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+const QCryptoAkCipherOptionsRSA *opt,
+QCryptoAkCipherKeyType type,
+const uint8_t *key,  size_t keylen,
+Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+switch (opts->alg) {
+case QCRYPTO_AKCIPHER_ALG_RSA:
+return qcrypto_nettle_rsa_new(>u.rsa, type, key, keylen, errp);
+
+default:
+error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+return NULL;
+}
+
+return NULL;
+}
+
+static void qcrypto_nettle_rsa_set_akcipher_size(QCryptoAkCipher *akcipher,
+ int key_size)
+{
+akcipher->max_plaintext_len = key_size;
+akcipher->max_ciphertext_len = key_size;
+akcipher->max_signature_len = key_size;
+akcipher->max_dgst_len = key_size;
+}
+
+static int qcrypt_nettle_parse_rsa_private_key(QCryptoNettleRSA *rsa,
+   const uint8_t *key,
+   size_t keylen)
+{
+g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen);
+
+if (!rsa_key) {
+return -1;
+}
+
+nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.d, rsa_key->d.len, 
rsa_key->d.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.p, rsa_key->p.len, 
rsa_key->p.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.q, rsa_key->q.len, 
rsa_key->q.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.a, rsa_key->dp.len,
+  rsa_key->dp.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.b, rsa_key->dq.len,
+  rsa_key->dq.data);
+nettle_mpz_init_set_str_256_u(rsa->priv.c, rsa_key->u.len, 
rsa_key->u.data);
+
+if (!rsa_public_key_pre

[PATCH v5 4/9] crypto: add ASN.1 DER decoder

2022-04-28 Thread zhenwei pi
From: Lei He 

Add an ANS.1 DER decoder which is used to parse asymmetric
cipher keys

Signed-off-by: zhenwei pi 
Signed-off-by: lei he 
---
 crypto/der.c | 190 +++
 crypto/der.h |  82 ++
 crypto/meson.build   |   1 +
 tests/unit/meson.build   |   1 +
 tests/unit/test-crypto-der.c | 290 +++
 5 files changed, 564 insertions(+)
 create mode 100644 crypto/der.c
 create mode 100644 crypto/der.h
 create mode 100644 tests/unit/test-crypto-der.c

diff --git a/crypto/der.c b/crypto/der.c
new file mode 100644
index 00..7907bcfd51
--- /dev/null
+++ b/crypto/der.c
@@ -0,0 +1,190 @@
+/*
+ * QEMU Crypto ASN.1 DER decoder
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include 
+#include 
+
+#include "crypto/der.h"
+
+enum QCryptoDERTypeTag {
+QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
+QCRYPTO_DER_TYPE_TAG_INT = 0x2,
+QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
+QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
+QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
+QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
+QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
+QCRYPTO_DER_TYPE_TAG_SET = 0x11,
+};
+
+#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
+#define QCRYPTO_DER_SHORT_LEN_MASK 0x80
+
+static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
+{
+return **data;
+}
+
+static void qcrypto_der_cut_nbytes(const uint8_t **data,
+   size_t *dlen,
+   size_t nbytes)
+{
+*data += nbytes;
+*dlen -= nbytes;
+}
+
+static uint8_t qcrypto_der_cut_byte(const uint8_t **data, size_t *dlen)
+{
+uint8_t val = qcrypto_der_peek_byte(data, dlen);
+
+qcrypto_der_cut_nbytes(data, dlen, 1);
+
+return val;
+}
+
+static int qcrypto_der_invoke_callback(DERDecodeCb cb, void *ctx,
+   const uint8_t *value, size_t vlen,
+   Error **errp)
+{
+if (!cb) {
+return 0;
+}
+
+return cb(ctx, value, vlen, errp);
+}
+
+static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t 
*dlen,
+ DERDecodeCb cb, void *ctx,
+ Error **errp)
+{
+const uint8_t *value;
+size_t vlen = 0;
+uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);
+
+/* short format of definite-length */
+if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
+if (byte_count > *dlen) {
+error_setg(errp, "Invalid content length: %u", byte_count);
+return -1;
+}
+
+value = *data;
+vlen = byte_count;
+qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+return -1;
+}
+return vlen;
+}
+
+/* Ignore highest bit */
+byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;
+
+/*
+ * size_t is enough to store the value of length, although the DER
+ * encoding standard supports larger length.
+ */
+if (byte_count > sizeof(size_t)) {
+error_setg(errp, "Invalid byte count of content length: %u",
+   byte_count);
+return -1;
+}
+
+if (*dlen < byte_count) {
+error_setg(errp, "Invalid content length: %u", byte_count);
+return -1;
+}
+while (byte_count--) {
+vlen <<= 8;
+vlen += qcrypto_der_cut_byte(data, dlen);
+}
+
+if (vlen > *dlen) {
+error_setg(errp, "Invalid content length: %lu", vlen);
+return -1;
+}
+
+value = *data;
+qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+return -1;
+}
+return vlen;
+}
+
+static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
+DERDecodeCb cb, void *ctx, Error **errp)
+{
+uint8_t val;
+if (*dlen < 1) {
+error_setg(errp, "Need more data");
+return -1;
+}
+val = qcrypto_der_peek_byte(data, dlen);
+
+/* must use definite length format */
+if (val == 

[PATCH v5 3/9] crypto: Introduce akcipher crypto class

2022-04-28 Thread zhenwei pi
Introduce new akcipher crypto class 'QCryptoAkCIpher', which supports
basic asymmetric operations: encrypt, decrypt, sign and verify.

Suggested by Daniel P. Berrangé, also add autoptr cleanup for the new
class. Thanks to Daniel!

Co-developed-by: lei he 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 crypto/akcipher.c | 102 
 crypto/akcipherpriv.h |  55 +
 crypto/meson.build|   1 +
 include/crypto/akcipher.h | 158 ++
 4 files changed, 316 insertions(+)
 create mode 100644 crypto/akcipher.c
 create mode 100644 crypto/akcipherpriv.h
 create mode 100644 include/crypto/akcipher.h

diff --git a/crypto/akcipher.c b/crypto/akcipher.c
new file mode 100644
index 00..ab28bf415b
--- /dev/null
+++ b/crypto/akcipher.c
@@ -0,0 +1,102 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/akcipher.h"
+#include "akcipherpriv.h"
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+  QCryptoAkCipherKeyType type,
+  const uint8_t *key, size_t keylen,
+  Error **errp)
+{
+QCryptoAkCipher *akcipher = NULL;
+
+return akcipher;
+}
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+return false;
+}
+
+int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
+ const void *in, size_t in_len,
+ void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->encrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_decrypt(QCryptoAkCipher *akcipher,
+ const void *in, size_t in_len,
+ void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->decrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_sign(QCryptoAkCipher *akcipher,
+  const void *in, size_t in_len,
+  void *out, size_t out_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->sign(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_verify(QCryptoAkCipher *akcipher,
+const void *in, size_t in_len,
+const void *in2, size_t in2_len, Error **errp)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+return drv->verify(akcipher, in, in_len, in2, in2_len, errp);
+}
+
+int qcrypto_akcipher_max_plaintext_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_plaintext_len;
+}
+
+int qcrypto_akcipher_max_ciphertext_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_ciphertext_len;
+}
+
+int qcrypto_akcipher_max_signature_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_signature_len;
+}
+
+int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher)
+{
+return akcipher->max_dgst_len;
+}
+
+void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
+{
+const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+drv->free(akcipher);
+}
diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h
new file mode 100644
index 00..739f639bcf
--- /dev/null
+++ b/crypto/akcipherpriv.h
@@ -0,0 +1,55 @@
+/*
+ * QEMU Crypto asymmetric algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public

[PATCH v5 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi

2022-04-28 Thread zhenwei pi
From: Lei He 

Introduce akcipher types, also include RSA related types.

Reviewed-by: Daniel P. Berrangé 
Signed-off-by: Lei He 
Signed-off-by: zhenwei pi 
---
 qapi/crypto.json | 64 
 1 file changed, 64 insertions(+)

diff --git a/qapi/crypto.json b/qapi/crypto.json
index 1ec54c15ca..f7bb9a42d0 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -540,3 +540,67 @@
   'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] },
 '*sanity-check': 'bool',
 '*passwordid': 'str' } }
+##
+# @QCryptoAkCipherAlgorithm:
+#
+# The supported algorithms for asymmetric encryption ciphers
+#
+# @rsa: RSA algorithm
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherAlgorithm',
+  'prefix': 'QCRYPTO_AKCIPHER_ALG',
+  'data': ['rsa']}
+
+##
+# @QCryptoAkCipherKeyType:
+#
+# The type of asymmetric keys.
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherKeyType',
+  'prefix': 'QCRYPTO_AKCIPHER_KEY_TYPE',
+  'data': ['public', 'private']}
+
+##
+# @QCryptoRSAPaddingAlgorithm:
+#
+# The padding algorithm for RSA.
+#
+# @raw: no padding used
+# @pkcs1: pkcs1#v1.5
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoRSAPaddingAlgorithm',
+  'prefix': 'QCRYPTO_RSA_PADDING_ALG',
+  'data': ['raw', 'pkcs1']}
+
+##
+# @QCryptoAkCipherOptionsRSA:
+#
+# Specific parameters for RSA algorithm.
+#
+# @hash-alg: QCryptoHashAlgorithm
+# @padding-alg: QCryptoRSAPaddingAlgorithm
+#
+# Since: 7.1
+##
+{ 'struct': 'QCryptoAkCipherOptionsRSA',
+  'data': { 'hash-alg':'QCryptoHashAlgorithm',
+'padding-alg': 'QCryptoRSAPaddingAlgorithm'}}
+
+##
+# @QCryptoAkCipherOptions:
+#
+# The options that are available for all asymmetric key algorithms
+# when creating a new QCryptoAkCipher.
+#
+# Since: 7.1
+##
+{ 'union': 'QCryptoAkCipherOptions',
+  'base': { 'alg': 'QCryptoAkCipherAlgorithm' },
+  'discriminator': 'alg',
+  'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

[PATCH v5 1/9] virtio-crypto: header update

2022-04-28 Thread zhenwei pi
Update header from linux, support akcipher service.

Reviewed-by: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 .../standard-headers/linux/virtio_crypto.h| 82 ++-
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/include/standard-headers/linux/virtio_crypto.h 
b/include/standard-headers/linux/virtio_crypto.h
index 5ff0b4ee59..68066dafb6 100644
--- a/include/standard-headers/linux/virtio_crypto.h
+++ b/include/standard-headers/linux/virtio_crypto.h
@@ -37,6 +37,7 @@
 #define VIRTIO_CRYPTO_SERVICE_HASH   1
 #define VIRTIO_CRYPTO_SERVICE_MAC2
 #define VIRTIO_CRYPTO_SERVICE_AEAD   3
+#define VIRTIO_CRYPTO_SERVICE_AKCIPHER 4
 
 #define VIRTIO_CRYPTO_OPCODE(service, op)   (((service) << 8) | (op))
 
@@ -57,6 +58,10 @@ struct virtio_crypto_ctrl_header {
   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x02)
 #define VIRTIO_CRYPTO_AEAD_DESTROY_SESSION \
   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x03)
+#define VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION \
+  VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x04)
+#define VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION \
+  VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x05)
uint32_t opcode;
uint32_t algo;
uint32_t flag;
@@ -180,6 +185,58 @@ struct virtio_crypto_aead_create_session_req {
uint8_t padding[32];
 };
 
+struct virtio_crypto_rsa_session_para {
+#define VIRTIO_CRYPTO_RSA_RAW_PADDING   0
+#define VIRTIO_CRYPTO_RSA_PKCS1_PADDING 1
+   uint32_t padding_algo;
+
+#define VIRTIO_CRYPTO_RSA_NO_HASH   0
+#define VIRTIO_CRYPTO_RSA_MD2   1
+#define VIRTIO_CRYPTO_RSA_MD3   2
+#define VIRTIO_CRYPTO_RSA_MD4   3
+#define VIRTIO_CRYPTO_RSA_MD5   4
+#define VIRTIO_CRYPTO_RSA_SHA1  5
+#define VIRTIO_CRYPTO_RSA_SHA2566
+#define VIRTIO_CRYPTO_RSA_SHA3847
+#define VIRTIO_CRYPTO_RSA_SHA5128
+#define VIRTIO_CRYPTO_RSA_SHA2249
+   uint32_t hash_algo;
+};
+
+struct virtio_crypto_ecdsa_session_para {
+#define VIRTIO_CRYPTO_CURVE_UNKNOWN   0
+#define VIRTIO_CRYPTO_CURVE_NIST_P192 1
+#define VIRTIO_CRYPTO_CURVE_NIST_P224 2
+#define VIRTIO_CRYPTO_CURVE_NIST_P256 3
+#define VIRTIO_CRYPTO_CURVE_NIST_P384 4
+#define VIRTIO_CRYPTO_CURVE_NIST_P521 5
+   uint32_t curve_id;
+   uint32_t padding;
+};
+
+struct virtio_crypto_akcipher_session_para {
+#define VIRTIO_CRYPTO_NO_AKCIPHER0
+#define VIRTIO_CRYPTO_AKCIPHER_RSA   1
+#define VIRTIO_CRYPTO_AKCIPHER_DSA   2
+#define VIRTIO_CRYPTO_AKCIPHER_ECDSA 3
+   uint32_t algo;
+
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC  1
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE 2
+   uint32_t keytype;
+   uint32_t keylen;
+
+   union {
+   struct virtio_crypto_rsa_session_para rsa;
+   struct virtio_crypto_ecdsa_session_para ecdsa;
+   } u;
+};
+
+struct virtio_crypto_akcipher_create_session_req {
+   struct virtio_crypto_akcipher_session_para para;
+   uint8_t padding[36];
+};
+
 struct virtio_crypto_alg_chain_session_para {
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER  1
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH  2
@@ -247,6 +304,8 @@ struct virtio_crypto_op_ctrl_req {
mac_create_session;
struct virtio_crypto_aead_create_session_req
aead_create_session;
+   struct virtio_crypto_akcipher_create_session_req
+   akcipher_create_session;
struct virtio_crypto_destroy_session_req
destroy_session;
uint8_t padding[56];
@@ -266,6 +325,14 @@ struct virtio_crypto_op_header {
VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x00)
 #define VIRTIO_CRYPTO_AEAD_DECRYPT \
VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_ENCRYPT \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00)
+#define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_SIGN \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02)
+#define VIRTIO_CRYPTO_AKCIPHER_VERIFY \
+   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x03)
uint32_t opcode;
/* algo should be service-specific algorithms */
uint32_t algo;
@@ -390,6 +457,16 @@ struct virtio_crypto_aead_data_req {
uint8_t padding[32];
 };
 
+struct virtio_crypto_akcipher_para {
+   uint32_t src_data_len;
+   uint32_t dst_data_len;
+};
+
+struct virtio_crypto_akcipher_data_req {
+   struct virtio_crypto_akcipher_para para;
+   uint8_t padding[40];
+};
+
 /* The request of the data virtqueue's packet */
 struct virtio_crypto_op_data_req {
struct virtio_crypto_op_header header;
@@ -399,6 +476,7 @@ struct virtio_crypto_op_data_req {
struct virtio_crypto_hash_data_req ha

[PATCH v5 0/9] Introduce akcipher service for virtio-crypto

2022-04-28 Thread zhenwei pi
Hi, Lei & MST

Daniel has started to review the akcipher framework and nettle & gcrypt
implementation, this part seems to be ready soon. Thanks a lot to Daniel!

And the last patch "crypto: Introduce RSA algorithm" handles akcipher
requests from guest and uses the new akcipher service. The new feature
can be used to test by the builtin driver. I would appreciate it if you
could review patch.

v4 -> v5:
- Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
- Rename asn1_decoder.c to der.c.
- Code style fix: use 'cleanup' & 'error' lables.
- Allow autoptr type to auto-free.
- Add test cases for rsakey to handle DER error.
- Other minor fixes.

v3 -> v4:
- Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
XXX-alg -> XXX-algo.
- Change version info in qapi/crypto.json, from 7.0 -> 7.1.
- Remove ecdsa from qapi/crypto.json, it would be introduced with the 
implemetion later.
- Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in 
qapi/crypto.json.
- Rename arguments of qcrypto_akcipher_XXX to keep aligned with 
qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add 
qcrypto_akcipher_max_XXX APIs.
- Add new API: qcrypto_akcipher_supports.
- Change the return value of qcrypto_akcipher_enc/dec/sign, these functions 
return the actual length of result.
- Separate ASN.1 source code and test case clean.
- Disable RSA raw encoding for akcipher-nettle.
- Separate RSA key parser into rsakey.{hc}, and implememts it with 
builtin-asn1-decoder and nettle respectivly.
- Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher 
priority than nettle.
- For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of 
returned result maybe less than the dst buffer size, return the actual length 
of result instead of the buffer length to the guest side. (in function 
virtio_crypto_akcipher_input_data_helper)
- Other minor changes.

Thanks to Daniel!

Eric pointed out this missing part of use case, send it here again.

In our plan, the feature is designed for HTTPS offloading case and other 
applications which use kernel RSA/ecdsa by keyctl syscall. The full picture 
shows bellow:


  Nginx/openssl[1] ... Apps
Guest   -
   virtio-crypto driver[2]
-
   virtio-crypto backend[3]
Host-
  /  |  \
  builtin[4]   vhost keyctl[5] ...


[1] User applications can offload RSA calculation to kernel by keyctl syscall. 
There is no keyctl engine in openssl currently, we developed a engine and tried 
to contribute it to openssl upstream, but openssl 1.x does not accept new 
feature. Link:
https://github.com/openssl/openssl/pull/16689

This branch is available and maintained by Lei 
https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine

We tested nginx(change config file only) with openssl keyctl engine, it works 
fine.

[2] virtio-crypto driver is used to communicate with host side, send requests 
to host side to do asymmetric calculation.
https://lkml.org/lkml/2022/3/1/1425

[3] virtio-crypto backend handles requests from guest side, and forwards 
request to crypto backend driver of QEMU.

[4] Currently RSA is supported only in builtin driver. This driver is supposed 
to test the full feature without other software(Ex vhost process) and hardware 
dependence. ecdsa is introduced into qapi type without implementation, this may 
be implemented in Q3-2022 or later. If ecdsa type definition should be added 
with the implementation together, I'll remove this in next version.

[5] keyctl backend is in development, we will post this feature in Q2-2022. 
keyctl backend can use hardware acceleration(Ex, Intel QAT).

Setup the full environment, tested with Intel QAT on host side, the QPS of 
HTTPS increase to ~200% in a guest.

VS PCI passthrough: the most important benefit of this solution makes the VM 
migratable.

v2 -> v3:
- Introduce akcipher types to qapi
- Add test/benchmark suite for akcipher class
- Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
  - crypto: Introduce akcipher crypto class
  - virtio-crypto: Introduce RSA algorithm

v1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Lei He (6):
  qapi: crypto-akcipher: Introduce akcipher types to qapi
  crypto: add ASN.1 DER decoder
  crypto: Implement RSA algorithm by hogweed
  crypto: Implement RSA algorithm by gcrypt
  test/crypto: Add test suite for crypto akcipher
  tests/crypto: Add test suite for RSA keys

Zhenwei Pi (3):
  virtio-crypto: header update
  crypto: Introduce akcipher crypt

Re: Re: [PATCH v4 1/5] virtio-crypto: change code style

2022-04-26 Thread zhenwei pi




On 4/26/22 14:12, Jason Wang wrote:

On Sun, Apr 24, 2022 at 6:45 PM zhenwei pi  wrote:


Use temporary variable to make code easy to read and maintain.
 /* Pad cipher's parameters */
 vcrypto->ctrl.u.sym_create_session.op_type =
 cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
 vcrypto->ctrl.u.sym_create_session.u.cipher.para.algo =
 vcrypto->ctrl.header.algo;
 vcrypto->ctrl.u.sym_create_session.u.cipher.para.keylen =
 cpu_to_le32(keylen);
 vcrypto->ctrl.u.sym_create_session.u.cipher.para.op =
 cpu_to_le32(op);
-->
 sym_create_session = >u.sym_create_session;
 sym_create_session->op_type = cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
 sym_create_session->u.cipher.para.algo = ctrl->header.algo;
 sym_create_session->u.cipher.para.keylen = cpu_to_le32(keylen);
 sym_create_session->u.cipher.para.op = cpu_to_le32(op);

The new style shows more obviously:
- the variable we want to operate.
- an assignment statement in a single line.


Still hundreds of lines of changes, I'd leave this change to other
mainters to dedice.

Thanks



Thanks to Jason!

Hi, Lei

What's your opinion?



Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
  .../virtio/virtio_crypto_akcipher_algs.c  | 40 ++-
  .../virtio/virtio_crypto_skcipher_algs.c  | 72 +--
  2 files changed, 59 insertions(+), 53 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index f3ec9420215e..20901a263fc8 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -106,23 +106,27 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
 unsigned int inlen;
 int err;
 unsigned int num_out = 0, num_in = 0;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_session_input *input;

 pkey = kmemdup(key, keylen, GFP_ATOMIC);
 if (!pkey)
 return -ENOMEM;

 spin_lock(>ctrl_lock);
-   memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-   memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-   vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+   ctrl = >ctrl;
+   memcpy(>header, header, sizeof(ctrl->header));
+   memcpy(>u, para, sizeof(ctrl->u));
+   input = >input;
+   input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);

-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_init_one(_sg, ctrl, sizeof(*ctrl));
 sgs[num_out++] = _sg;

 sg_init_one(_sg, pkey, keylen);
 sgs[num_out++] = _sg;

-   sg_init_one(_sg, >input, sizeof(vcrypto->input));
+   sg_init_one(_sg, input, sizeof(*input));
 sgs[num_out + num_in++] = _sg;

 err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
@@ -134,12 +138,12 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
!virtqueue_is_broken(vcrypto->ctrl_vq))
 cpu_relax();

-   if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+   if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
 err = -EINVAL;
 goto out;
 }

-   ctx->session_id = le64_to_cpu(vcrypto->input.session_id);
+   ctx->session_id = le64_to_cpu(input->session_id);
 ctx->session_valid = true;
 err = 0;

@@ -149,7 +153,7 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher

 if (err < 0)
 pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(vcrypto->input.status));
+   le32_to_cpu(input->status));

 return err;
  }
@@ -161,23 +165,27 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
 struct virtio_crypto *vcrypto = ctx->vcrypto;
 unsigned int num_out = 0, num_in = 0, inlen;
 int err;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_inhdr *ctrl_status;

 spin_lock(>ctrl_lock);
 if (!ctx->session_valid) {
 err = 0;
 goto out;
 }
-   vcrypto->ctrl_status.status = VIRTIO_CRYPTO_ERR;
-   vcrypto->ctrl.header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
-   vcrypto->ctrl.header.queue_id = 0;
+   ctrl_status = >ctrl_status;
+   ctrl_status->status = VIRTIO_CRYPTO_ERR;
+   ctrl = >ctrl;
+   ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
+ 

Re: Re: Re: [PATCH v3 1/5] virtio-crypto: use private buffer for control request

2022-04-24 Thread zhenwei pi

On 4/24/22 14:21, Jason Wang wrote:

On Fri, Apr 22, 2022 at 5:12 PM zhenwei pi  wrote:


On 4/22/22 15:41, Jason Wang wrote:


在 2022/4/21 18:40, zhenwei pi 写道:

Originally, all of the control requests share a single buffer(
ctrl & input & ctrl_status fields in struct virtio_crypto), this
allows queue depth 1 only, the performance of control queue gets
limited by this design.

In this patch, each request allocates request buffer dynamically, and
free buffer after request, it's possible to optimize control queue
depth in the next step.

A necessary comment is already in code, still describe it again:
/*
   * Note: there are padding fields in request, clear them to zero before
   * sending to host,
   * Ex, virtio_crypto_ctrl_request::ctrl::u::destroy_session::padding[48]
   */
So use kzalloc to allocate buffer of struct virtio_crypto_ctrl_request.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
   drivers/crypto/virtio/Makefile|   1 +
   .../virtio/virtio_crypto_akcipher_algs.c  |  90 ++--
   drivers/crypto/virtio/virtio_crypto_common.c  |  39 +



Any reason we can't use virtio_crypto_core.c?


Another patch in this series: [PATCH v3 3/5] virtio-crypto: move helpers
into virtio_crypto_common.c

Move virtcrypto_clear_request and virtcrypto_dataq_callback into
virtio_crypto_common.c to make code clear. Then the xx_core.c
supports:
- probe/remove/irq affinity seting for a virtio device
- basic virtio related operations

xx_common.c supports:
- common helpers/functions for algos

So I put this into a new file.


I don't see obvious differences but we can leave it to the
virtio-crypto maintainers to decide.



OK!





   drivers/crypto/virtio/virtio_crypto_common.h  |  19 ++-
   .../virtio/virtio_crypto_skcipher_algs.c  | 133 --
   5 files changed, 156 insertions(+), 126 deletions(-)
   create mode 100644 drivers/crypto/virtio/virtio_crypto_common.c

diff --git a/drivers/crypto/virtio/Makefile
b/drivers/crypto/virtio/Makefile
index bfa6cbae342e..49c1fa80e465 100644
--- a/drivers/crypto/virtio/Makefile
+++ b/drivers/crypto/virtio/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio_crypto.o
   virtio_crypto-objs := \
   virtio_crypto_skcipher_algs.o \
   virtio_crypto_akcipher_algs.o \
+virtio_crypto_common.o \
   virtio_crypto_mgr.o \
   virtio_crypto_core.o
diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index f3ec9420215e..9561bc2df62b 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -102,8 +102,8 @@ static int
virtio_crypto_alg_akcipher_init_session(struct virtio_crypto_akcipher
   {
   struct scatterlist outhdr_sg, key_sg, inhdr_sg, *sgs[3];
   struct virtio_crypto *vcrypto = ctx->vcrypto;
+struct virtio_crypto_ctrl_request *vc_ctrl_req;
   uint8_t *pkey;
-unsigned int inlen;
   int err;
   unsigned int num_out = 0, num_in = 0;
@@ -111,98 +111,91 @@ static int
virtio_crypto_alg_akcipher_init_session(struct virtio_crypto_akcipher
   if (!pkey)
   return -ENOMEM;
-spin_lock(>ctrl_lock);
-memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+if (!vc_ctrl_req) {
+err = -ENOMEM;
+goto out;
+}
-sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+memcpy(_ctrl_req->ctrl.header, header,
sizeof(vc_ctrl_req->ctrl.header));
+memcpy(_ctrl_req->ctrl.u, para, sizeof(vc_ctrl_req->ctrl.u));
+sg_init_one(_sg, _ctrl_req->ctrl,
sizeof(vc_ctrl_req->ctrl));
   sgs[num_out++] = _sg;
   sg_init_one(_sg, pkey, keylen);
   sgs[num_out++] = _sg;
-sg_init_one(_sg, >input, sizeof(vcrypto->input));
+vc_ctrl_req->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);



Nit: if there's no special reason, let's move this after the above
memcpys as what's done previously.



+sg_init_one(_sg, _ctrl_req->input,
sizeof(vc_ctrl_req->input));
   sgs[num_out + num_in++] = _sg;
-err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in,
vcrypto, GFP_ATOMIC);
+err = virtio_crypto_ctrl_vq_request(vcrypto, sgs, num_out,
num_in, vc_ctrl_req);



I'd split this into a separate patch.



OK!



   if (err < 0)
   goto out;
-virtqueue_kick(vcrypto->ctrl_vq);
-while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
-   !virtqueue_is_broken(vcrypto->ctrl_vq))
-cpu_relax();
-
-if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+if (le32_to_cpu(vc_ctrl_req->input.status) != VIRTIO_CRYPTO_OK) {
+pr_err("virtio_crypto: Cre

[PATCH v4 5/5] virtio-crypto: enable retry for virtio-crypto-dev

2022-04-24 Thread zhenwei pi
From: lei he 

Enable retry for virtio-crypto-dev, so that crypto-engine
can process cipher-requests parallelly.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_core.c 
b/drivers/crypto/virtio/virtio_crypto_core.c
index 60490ffa3df1..f67e0d4c1b0c 100644
--- a/drivers/crypto/virtio/virtio_crypto_core.c
+++ b/drivers/crypto/virtio/virtio_crypto_core.c
@@ -144,7 +144,8 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi)
spin_lock_init(>data_vq[i].lock);
vi->data_vq[i].vq = vqs[i];
/* Initialize crypto engine */
-   vi->data_vq[i].engine = crypto_engine_alloc_init(dev, 1);
+   vi->data_vq[i].engine = crypto_engine_alloc_init_and_set(dev, 
true, NULL, 1,
+   
virtqueue_get_vring_size(vqs[i]));
if (!vi->data_vq[i].engine) {
ret = -ENOMEM;
goto err_engine;
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v4 4/5] virtio-crypto: adjust dst_len at ops callback

2022-04-24 Thread zhenwei pi
From: lei he 

For some akcipher operations(eg, decryption of pkcs1pad(rsa)),
the length of returned result maybe less than akcipher_req->dst_len,
we need to recalculate the actual dst_len through the virt-queue
protocol.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: lei he 
Signed-off-by: zhenwei pi 
---
 drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 1e98502830cf..1892901d2a71 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -90,9 +90,12 @@ static void virtio_crypto_dataq_akcipher_callback(struct 
virtio_crypto_request *
}
 
akcipher_req = vc_akcipher_req->akcipher_req;
-   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY)
+   if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+   /* actuall length maybe less than dst buffer */
+   akcipher_req->dst_len = len - sizeof(vc_req->status);
sg_copy_from_buffer(akcipher_req->dst, 
sg_nents(akcipher_req->dst),
vc_akcipher_req->dst_buf, 
akcipher_req->dst_len);
+   }
virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, 
error);
 }
 
-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


[PATCH v4 3/5] virtio-crypto: wait ctrl queue instead of busy polling

2022-04-24 Thread zhenwei pi
Originally, after submitting request into virtio crypto control
queue, the guest side polls the result from the virt queue. This
works like following:
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  busy poll virtqueue
   |
  spin_unlock(>ctrl_lock)
  ...

There are two problems:
1, The queue depth is always 1, the performance of a virtio crypto
   device gets limited. Multi user processes share a single control
   queue, and hit spin lock race from control queue. Test on Intel
   Platinum 8260, a single worker gets ~35K/s create/close session
   operations, and 8 workers get ~40K/s operations with 800% CPU
   utilization.
2, The control request is supposed to get handled immediately, but
   in the current implementation of QEMU(v6.2), the vCPU thread kicks
   another thread to do this work, the latency also gets unstable.
   Tracking latency of virtio_crypto_alg_akcipher_close_session in 5s:
usecs   : count distribution
 0 -> 1  : 0||
 2 -> 3  : 7||
 4 -> 7  : 72   ||
 8 -> 15 : 186485   ||
16 -> 31 : 687  ||
32 -> 63 : 5||
64 -> 127: 3||
   128 -> 255: 1||
   256 -> 511: 0||
   512 -> 1023   : 0||
  1024 -> 2047   : 0||
  2048 -> 4095   : 0||
  4096 -> 8191   : 0||
  8192 -> 16383  : 2||
This means that a CPU may hold vcrypto->ctrl_lock as long as 8192~16383us.

To improve the performance of control queue, a request on control queue
waits completion instead of busy polling to reduce lock racing, and gets
completed by control queue callback.
CPU0   CPU1   ... CPUx  CPUy
 |  |  | |
 \  \  / /
  \spin_lock(>ctrl_lock)---/
   |
 virtqueue add & kick
   |
  -spin_unlock(>ctrl_lock)--
 /  /  \ \
 |  |  | |
wait   wait   wait  wait

Test this patch, the guest side get ~200K/s operations with 300% CPU
utilization.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 29 ++-
 drivers/crypto/virtio/virtio_crypto_common.h  |  4 ++
 drivers/crypto/virtio/virtio_crypto_core.c| 52 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 34 ++--
 4 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 509884e8b201..1e98502830cf 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -103,7 +103,6 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
struct scatterlist outhdr_sg, key_sg, inhdr_sg, *sgs[3];
struct virtio_crypto *vcrypto = ctx->vcrypto;
uint8_t *pkey;
-   unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
@@ -135,18 +134,9 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
-   spin_lock(>ctrl_lock);
-   err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0) {
-   spin_unlock(>ctrl_lock);
+   err = virtio_crypto_ctrl_vq_request(vcrypto, sgs, num_out, num_in, 
vc_ctrl_req);
+   if (err < 0)
goto out;
-   }
-
-   virtqueue_kick(vcrypto->ctrl_vq);
-   while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
-  !virtqueue_is_broken(vcrypto->ctrl_vq))
-   cpu_relax();
-   spin_unlock(>ctrl_lock);
 
if (le32_to_

[PATCH v4 2/5] virtio-crypto: use private buffer for control request

2022-04-24 Thread zhenwei pi
Originally, all of the control requests share a single buffer(
ctrl & input & ctrl_status fields in struct virtio_crypto), this
allows queue depth 1 only, the performance of control queue gets
limited by this design.

In this patch, each request allocates request buffer dynamically, and
free buffer after request, so the scope protected by ctrl_lock also
get optimized here.
It's possible to optimize control queue depth in the next step.

A necessary comment is already in code, still describe it again:
/*
 * Note: there are padding fields in request, clear them to zero before
 * sending to host to avoid to divulge any information.
 * Ex, virtio_crypto_ctrl_request::ctrl::u::destroy_session::padding[48]
 */
So use kzalloc to allocate buffer of struct virtio_crypto_ctrl_request.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 41 +++
 drivers/crypto/virtio/virtio_crypto_common.h  | 17 +--
 .../virtio/virtio_crypto_skcipher_algs.c  | 50 ---
 3 files changed, 75 insertions(+), 33 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index 20901a263fc8..509884e8b201 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -108,16 +108,22 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int num_out = 0, num_in = 0;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_session_input *input;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
-   spin_lock(>ctrl_lock);
-   ctrl = >ctrl;
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req) {
+   err = -ENOMEM;
+   goto out;
+   }
+
+   ctrl = _ctrl_req->ctrl;
memcpy(>header, header, sizeof(ctrl->header));
memcpy(>u, para, sizeof(ctrl->u));
-   input = >input;
+   input = _ctrl_req->input;
input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
sg_init_one(_sg, ctrl, sizeof(*ctrl));
@@ -129,14 +135,18 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
+   spin_lock(>ctrl_lock);
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0)
+   if (err < 0) {
+   spin_unlock(>ctrl_lock);
goto out;
+   }
 
virtqueue_kick(vcrypto->ctrl_vq);
while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
+   spin_unlock(>ctrl_lock);
 
if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
err = -EINVAL;
@@ -148,7 +158,7 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
err = 0;
 
 out:
-   spin_unlock(>ctrl_lock);
+   kfree(vc_ctrl_req);
kfree_sensitive(pkey);
 
if (err < 0)
@@ -167,15 +177,22 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
int err;
struct virtio_crypto_op_ctrl_req *ctrl;
struct virtio_crypto_inhdr *ctrl_status;
+   struct virtio_crypto_ctrl_request *vc_ctrl_req;
 
-   spin_lock(>ctrl_lock);
if (!ctx->session_valid) {
err = 0;
goto out;
}
-   ctrl_status = >ctrl_status;
+
+   vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+   if (!vc_ctrl_req) {
+   err = -ENOMEM;
+   goto out;
+   }
+
+   ctrl_status = _ctrl_req->ctrl_status;
ctrl_status->status = VIRTIO_CRYPTO_ERR;
-   ctrl = >ctrl;
+   ctrl = _ctrl_req->ctrl;
ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
ctrl->header.queue_id = 0;
 
@@ -188,14 +205,18 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
sg_init_one(_sg, _status->status, 
sizeof(ctrl_status->status));
sgs[num_out + num_in++] = _sg;
 
+   spin_lock(>ctrl_lock);
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
-   if (err < 0)
+   if (err < 0) {
+   spin_unlock(>ctrl_lock);
goto out;
+   }
 
virtqueue_kick(vcrypto->ctrl_vq);
while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
+   

[PATCH v4 1/5] virtio-crypto: change code style

2022-04-24 Thread zhenwei pi
Use temporary variable to make code easy to read and maintain.
/* Pad cipher's parameters */
vcrypto->ctrl.u.sym_create_session.op_type =
cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.algo =
vcrypto->ctrl.header.algo;
vcrypto->ctrl.u.sym_create_session.u.cipher.para.keylen =
cpu_to_le32(keylen);
vcrypto->ctrl.u.sym_create_session.u.cipher.para.op =
cpu_to_le32(op);
-->
sym_create_session = >u.sym_create_session;
sym_create_session->op_type = cpu_to_le32(VIRTIO_CRYPTO_SYM_OP_CIPHER);
sym_create_session->u.cipher.para.algo = ctrl->header.algo;
sym_create_session->u.cipher.para.keylen = cpu_to_le32(keylen);
sym_create_session->u.cipher.para.op = cpu_to_le32(op);

The new style shows more obviously:
- the variable we want to operate.
- an assignment statement in a single line.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
 .../virtio/virtio_crypto_akcipher_algs.c  | 40 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 72 +--
 2 files changed, 59 insertions(+), 53 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
index f3ec9420215e..20901a263fc8 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -106,23 +106,27 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
unsigned int inlen;
int err;
unsigned int num_out = 0, num_in = 0;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_session_input *input;
 
pkey = kmemdup(key, keylen, GFP_ATOMIC);
if (!pkey)
return -ENOMEM;
 
spin_lock(>ctrl_lock);
-   memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-   memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-   vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+   ctrl = >ctrl;
+   memcpy(>header, header, sizeof(ctrl->header));
+   memcpy(>u, para, sizeof(ctrl->u));
+   input = >input;
+   input->status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
 
-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_init_one(_sg, ctrl, sizeof(*ctrl));
sgs[num_out++] = _sg;
 
sg_init_one(_sg, pkey, keylen);
sgs[num_out++] = _sg;
 
-   sg_init_one(_sg, >input, sizeof(vcrypto->input));
+   sg_init_one(_sg, input, sizeof(*input));
sgs[num_out + num_in++] = _sg;
 
err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
@@ -134,12 +138,12 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
   !virtqueue_is_broken(vcrypto->ctrl_vq))
cpu_relax();
 
-   if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+   if (le32_to_cpu(input->status) != VIRTIO_CRYPTO_OK) {
err = -EINVAL;
goto out;
}
 
-   ctx->session_id = le64_to_cpu(vcrypto->input.session_id);
+   ctx->session_id = le64_to_cpu(input->session_id);
ctx->session_valid = true;
err = 0;
 
@@ -149,7 +153,7 @@ static int virtio_crypto_alg_akcipher_init_session(struct 
virtio_crypto_akcipher
 
if (err < 0)
pr_err("virtio_crypto: Create session failed status: %u\n",
-   le32_to_cpu(vcrypto->input.status));
+   le32_to_cpu(input->status));
 
return err;
 }
@@ -161,23 +165,27 @@ static int 
virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe
struct virtio_crypto *vcrypto = ctx->vcrypto;
unsigned int num_out = 0, num_in = 0, inlen;
int err;
+   struct virtio_crypto_op_ctrl_req *ctrl;
+   struct virtio_crypto_inhdr *ctrl_status;
 
spin_lock(>ctrl_lock);
if (!ctx->session_valid) {
err = 0;
goto out;
}
-   vcrypto->ctrl_status.status = VIRTIO_CRYPTO_ERR;
-   vcrypto->ctrl.header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
-   vcrypto->ctrl.header.queue_id = 0;
+   ctrl_status = >ctrl_status;
+   ctrl_status->status = VIRTIO_CRYPTO_ERR;
+   ctrl = >ctrl;
+   ctrl->header.opcode = 
cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION);
+   ctrl->header.queue_id = 0;
 
-   destroy_session = >ctrl.u.destroy_session;
+   destroy_session = >u.destroy_session;
destroy_session->session_id = cpu_to_le64(ctx->session_id);
 
-   sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+   sg_i

[PATCH v4 0/5] virtio-crypto: Improve performance

2022-04-24 Thread zhenwei pi
Hi, Lei
I'd like to move helper and callback functions(Eg, virtcrypto_clear_request
 and virtcrypto_ctrlq_callback) from xx_core.c to xx_common.c,
then the xx_core.c supports:
  - probe/remove/irq affinity seting for a virtio device
  - basic virtio related operations

xx_common.c supports:
  - common helpers/functions for algos

Do you have any suggestion about this?

v3 -> v4:
 - Don't create new file virtio_common.c, the new functions are added
   into virtio_crypto_core.c
 - Split the first patch into two parts:
 1, change code style,
 2, use private buffer instead of shared buffer
 - Remove relevant change.
 - Other minor changes.

v2 -> v3:
 - Jason suggested that spliting the first patch into two part:
 1, using private buffer
 2, remove the busy polling
   Rework as Jason's suggestion, this makes the smaller change in
   each one and clear.

v1 -> v2:
 - Use kfree instead of kfree_sensitive for insensitive buffer.
 - Several coding style fix.
 - Use memory from current node, instead of memory close to device
 - Add more message in commit, also explain why removing per-device
   request buffer.
 - Add necessary comment in code to explain why using kzalloc to
   allocate struct virtio_crypto_ctrl_request.

v1:
The main point of this series is to improve the performance for
virtio crypto:
- Use wait mechanism instead of busy polling for ctrl queue, this
  reduces CPU and lock racing, it's possiable to create/destroy session
  parallelly, QPS increases from ~40K/s to ~200K/s.
- Enable retry on crypto engine to improve performance for data queue,
  this allows the larger depth instead of 1.
- Fix dst data length in akcipher service.
- Other style fix.

lei he (2):
  virtio-crypto: adjust dst_len at ops callback
  virtio-crypto: enable retry for virtio-crypto-dev

zhenwei pi (3):
  virtio-crypto: change code style
  virtio-crypto: use private buffer for control request
  virtio-crypto: wait ctrl queue instead of busy polling

 .../virtio/virtio_crypto_akcipher_algs.c  |  83 ++-
 drivers/crypto/virtio/virtio_crypto_common.h  |  21 ++-
 drivers/crypto/virtio/virtio_crypto_core.c|  55 ++-
 .../virtio/virtio_crypto_skcipher_algs.c  | 140 --
 4 files changed, 180 insertions(+), 119 deletions(-)

-- 
2.20.1

___
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization


Re: Re: [PATCH v3 1/5] virtio-crypto: use private buffer for control request

2022-04-22 Thread zhenwei pi

On 4/22/22 15:41, Jason Wang wrote:


在 2022/4/21 18:40, zhenwei pi 写道:

Originally, all of the control requests share a single buffer(
ctrl & input & ctrl_status fields in struct virtio_crypto), this
allows queue depth 1 only, the performance of control queue gets
limited by this design.

In this patch, each request allocates request buffer dynamically, and
free buffer after request, it's possible to optimize control queue
depth in the next step.

A necessary comment is already in code, still describe it again:
/*
  * Note: there are padding fields in request, clear them to zero before
  * sending to host,
  * Ex, virtio_crypto_ctrl_request::ctrl::u::destroy_session::padding[48]
  */
So use kzalloc to allocate buffer of struct virtio_crypto_ctrl_request.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
  drivers/crypto/virtio/Makefile    |   1 +
  .../virtio/virtio_crypto_akcipher_algs.c  |  90 ++--
  drivers/crypto/virtio/virtio_crypto_common.c  |  39 +



Any reason we can't use virtio_crypto_core.c?

Another patch in this series: [PATCH v3 3/5] virtio-crypto: move helpers 
into virtio_crypto_common.c


Move virtcrypto_clear_request and virtcrypto_dataq_callback into
virtio_crypto_common.c to make code clear. Then the xx_core.c
supports:
  - probe/remove/irq affinity seting for a virtio device
  - basic virtio related operations

xx_common.c supports:
  - common helpers/functions for algos

So I put this into a new file.




  drivers/crypto/virtio/virtio_crypto_common.h  |  19 ++-
  .../virtio/virtio_crypto_skcipher_algs.c  | 133 --
  5 files changed, 156 insertions(+), 126 deletions(-)
  create mode 100644 drivers/crypto/virtio/virtio_crypto_common.c

diff --git a/drivers/crypto/virtio/Makefile 
b/drivers/crypto/virtio/Makefile

index bfa6cbae342e..49c1fa80e465 100644
--- a/drivers/crypto/virtio/Makefile
+++ b/drivers/crypto/virtio/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio_crypto.o
  virtio_crypto-objs := \
  virtio_crypto_skcipher_algs.o \
  virtio_crypto_akcipher_algs.o \
+    virtio_crypto_common.o \
  virtio_crypto_mgr.o \
  virtio_crypto_core.o
diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c 
b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c

index f3ec9420215e..9561bc2df62b 100644
--- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c
@@ -102,8 +102,8 @@ static int 
virtio_crypto_alg_akcipher_init_session(struct virtio_crypto_akcipher

  {
  struct scatterlist outhdr_sg, key_sg, inhdr_sg, *sgs[3];
  struct virtio_crypto *vcrypto = ctx->vcrypto;
+    struct virtio_crypto_ctrl_request *vc_ctrl_req;
  uint8_t *pkey;
-    unsigned int inlen;
  int err;
  unsigned int num_out = 0, num_in = 0;
@@ -111,98 +111,91 @@ static int 
virtio_crypto_alg_akcipher_init_session(struct virtio_crypto_akcipher

  if (!pkey)
  return -ENOMEM;
-    spin_lock(>ctrl_lock);
-    memcpy(>ctrl.header, header, sizeof(vcrypto->ctrl.header));
-    memcpy(>ctrl.u, para, sizeof(vcrypto->ctrl.u));
-    vcrypto->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);
+    vc_ctrl_req = kzalloc(sizeof(*vc_ctrl_req), GFP_KERNEL);
+    if (!vc_ctrl_req) {
+    err = -ENOMEM;
+    goto out;
+    }
-    sg_init_one(_sg, >ctrl, sizeof(vcrypto->ctrl));
+    memcpy(_ctrl_req->ctrl.header, header, 
sizeof(vc_ctrl_req->ctrl.header));

+    memcpy(_ctrl_req->ctrl.u, para, sizeof(vc_ctrl_req->ctrl.u));
+    sg_init_one(_sg, _ctrl_req->ctrl, 
sizeof(vc_ctrl_req->ctrl));

  sgs[num_out++] = _sg;
  sg_init_one(_sg, pkey, keylen);
  sgs[num_out++] = _sg;
-    sg_init_one(_sg, >input, sizeof(vcrypto->input));
+    vc_ctrl_req->input.status = cpu_to_le32(VIRTIO_CRYPTO_ERR);



Nit: if there's no special reason, let's move this after the above 
memcpys as what's done previously.



+    sg_init_one(_sg, _ctrl_req->input, 
sizeof(vc_ctrl_req->input));

  sgs[num_out + num_in++] = _sg;
-    err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, num_out, num_in, 
vcrypto, GFP_ATOMIC);
+    err = virtio_crypto_ctrl_vq_request(vcrypto, sgs, num_out, 
num_in, vc_ctrl_req);



I'd split this into a separate patch.



OK!



  if (err < 0)
  goto out;
-    virtqueue_kick(vcrypto->ctrl_vq);
-    while (!virtqueue_get_buf(vcrypto->ctrl_vq, ) &&
-   !virtqueue_is_broken(vcrypto->ctrl_vq))
-    cpu_relax();
-
-    if (le32_to_cpu(vcrypto->input.status) != VIRTIO_CRYPTO_OK) {
+    if (le32_to_cpu(vc_ctrl_req->input.status) != VIRTIO_CRYPTO_OK) {
+    pr_err("virtio_crypto: Create session failed status: %u\n",
+    le32_to_cpu(vc_ctrl_req->input.status));
  err = -EINVAL;
  goto out;
  }



Do we need a warning for -ENOMEM?



Memory(especially small siz

Re: Re: [PATCH v3 2/5] virtio-crypto: wait ctrl queue instead of busy polling

2022-04-22 Thread zhenwei pi

On 4/22/22 15:46, Jason Wang wrote:


在 2022/4/21 18:40, zhenwei pi 写道:

Originally, after submitting request into virtio crypto control
queue, the guest side polls the result from the virt queue. This
works like following:
 CPU0   CPU1   ... CPUx  CPUy
  |  |  | |
  \  \  / /
   \spin_lock(>ctrl_lock)---/
    |
  virtqueue add & kick
    |
   busy poll virtqueue
    |
   spin_unlock(>ctrl_lock)
   ...

There are two problems:
1, The queue depth is always 1, the performance of a virtio crypto
    device gets limited. Multi user processes share a single control
    queue, and hit spin lock race from control queue. Test on Intel
    Platinum 8260, a single worker gets ~35K/s create/close session
    operations, and 8 workers get ~40K/s operations with 800% CPU
    utilization.
2, The control request is supposed to get handled immediately, but
    in the current implementation of QEMU(v6.2), the vCPU thread kicks
    another thread to do this work, the latency also gets unstable.
    Tracking latency of virtio_crypto_alg_akcipher_close_session in 5s:
 usecs   : count distribution
  0 -> 1  : 0    |    |
  2 -> 3  : 7    |    |
  4 -> 7  : 72   |    |
  8 -> 15 : 186485   ||
 16 -> 31 : 687  |    |
 32 -> 63 : 5    |    |
 64 -> 127    : 3    |    |
    128 -> 255    : 1    |    |
    256 -> 511    : 0    |    |
    512 -> 1023   : 0    |    |
   1024 -> 2047   : 0    |    |
   2048 -> 4095   : 0    |    |
   4096 -> 8191   : 0    |    |
   8192 -> 16383  : 2    |    |
    This means that a CPU may hold vcrypto->ctrl_lock as long as 
8192~16383us.


To improve the performance of control queue, a request on control 
queue waits
completion instead of busy polling to reduce lock racing, and gets 
completed by

control queue callback.
 CPU0   CPU1   ... CPUx  CPUy
  |  |  | |
  \  \  / /
   \spin_lock(>ctrl_lock)---/
    |
  virtqueue add & kick
    |
   -spin_unlock(>ctrl_lock)--
  /  /  \ \
  |  |  | |
 wait   wait   wait  wait

Test this patch, the guest side get ~200K/s operations with 300% CPU
utilization.

Cc: Michael S. Tsirkin 
Cc: Jason Wang 
Cc: Gonglei 
Signed-off-by: zhenwei pi 
---
  drivers/crypto/virtio/virtio_crypto_common.c | 42 +++-
  drivers/crypto/virtio/virtio_crypto_common.h |  8 
  drivers/crypto/virtio/virtio_crypto_core.c   |  2 +-
  3 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/drivers/crypto/virtio/virtio_crypto_common.c 
b/drivers/crypto/virtio/virtio_crypto_common.c

index e65125a74db2..93df73c40dd3 100644
--- a/drivers/crypto/virtio/virtio_crypto_common.c
+++ b/drivers/crypto/virtio/virtio_crypto_common.c
@@ -8,14 +8,21 @@
  #include "virtio_crypto_common.h"
+static void virtio_crypto_ctrlq_callback(struct 
virtio_crypto_ctrl_request *vc_ctrl_req)

+{
+    complete(_ctrl_req->compl);
+}
+
  int virtio_crypto_ctrl_vq_request(struct virtio_crypto *vcrypto, 
struct scatterlist *sgs[],

    unsigned int out_sgs, unsigned int in_sgs,
    struct virtio_crypto_ctrl_request *vc_ctrl_req)
  {
  int err;
-    unsigned int inlen;
  unsigned long flags;
+    init_completion(_ctrl_req->compl);
+    vc_ctrl_req->ctrl_cb =  virtio_crypto_ctrlq_callback;



Is there a chance that the cb would not be virtio_crypto_ctrlq_callback()?



Yes, it's the only callback function used for control queue, removing 
this and calling virtio_crypto_ctrlq_callback directly in 
virtcrypto_ctrlq_callback seems better. I'll fix this in the next version.



+
  spin_lock_irqsave(>ctrl_lock, flags);
  err = virtqueue_add_sgs(vcrypto->ctrl_vq, sgs, out_sgs, in_sgs, 
vc_ctrl_req, GFP_ATOMIC);

  if (err < 0) {
@@ -24,16 +31,31 @@ int virtio_crypto_ctrl_vq_request(struct 
virtio_crypto *vcrypto, struct scatterl

  }
  vir

  1   2   >