RE: [PATCH] swiotlb: check set_memory_decrypted()'s return value

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> From: Robin Murphy <robin.murphy@xxxxxxx>
> Sent: Monday, November 21, 2022 12:49 PM
> > [...]
> > diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
> > @@ -248,12 +248,16 @@ void __init swiotlb_update_mem_attributes(void)
> >   	struct io_tlb_mem *mem = &io_tlb_default_mem;
> >   	void *vaddr;
> >   	unsigned long bytes;
> > +	int rc;
> >
> >   	if (!mem->nslabs || mem->late_alloc)
> >   		return;
> >   	vaddr = phys_to_virt(mem->start);
> >   	bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
> > -	set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
> > +
> > +	rc = set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
> > +	if (rc)
> > +		panic("Failed to decrypt swiotlb bounce buffers (%d)\n", rc);
> 
> Aww, I just cleaned up all the panics! AFAICS we could warn and set
Sorry, I didn't know about that... :-)

> mem->nslabs to 0 here to semi-gracefully disable SWIOTLB (presumably
> decryption failure is sufficiently unexpected that "leaking" the SWIOTLB
> memory is the least of the system's problems). Or indeed just warn and
> do nothing as in the swiotlb_init_late() case below - what's with the
> inconsistency? In either path we have the same expectation that
> decryption succeeds (or does nothing, successfully), so failure is no
> more or less fatal to later SWIOTLB operation depending on when the
> architecture chose to set it up.

I agree it's better to print an error message rather than panic here, but
anyway the kernel will panic later, e.g. when an AMD SEV-SNP guest runs
on Hyper-V, in case this set_memory_decrypted() fails, we set
mem->nslabs to 0, and next we'll hit a panic soon when the storage
driver calls swiotlb_tbl_map_single():
"Kernel panic - not syncing: Can not allocate SWIOTLB buffer earlier and 
can't now provide you with the DMA bounce buffer".

> So in 3 init paths we have 3 different outcomes from the same thing :(
> 
> 1: make the whole system unusable
> 2: continue with possible data corruption (or at least weird DMA errors)
> if devices still see encrypted memory contents
> 3: cleanly disable SWIOTLB, thus also all subsequent attempts to use it
> 
> (OK, for the rmem case this isn't actually 3 since falling back to
> io_tlb_default_mem might work out more like 2, but hopefully you get my
> point)
> 
> Thanks,
> Robin.

How do you like this new version: 
1) I removed the panic().
2) For swiotlb_update_mem_attributes() and swiotlb_init_late(), I print
an error message and disable swiotlb: the error seems so bad that IMO
we have to disable swiotlb.
3) No change to rmem_swiotlb_device_init(). The error in this function
doesn't seem fatal to me.

The bottom line is that set_memory_decrypted() should not fail silently
and cause weird issues later... BTW, normally IMO set_memory_decrypted()
doesn't fail, but I did see a failure because we're expectd to retry
(the failure is fixed by
https://lwn.net/ml/linux-kernel/20221121195151.21812-3-decui%40microsoft.com/)

--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -248,12 +248,20 @@ void __init swiotlb_update_mem_attributes(void)
        struct io_tlb_mem *mem = &io_tlb_default_mem;
        void *vaddr;
        unsigned long bytes;
+       int rc;
 
        if (!mem->nslabs || mem->late_alloc)
                return;
        vaddr = phys_to_virt(mem->start);
        bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
-       set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
+
+       rc = set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
+       if (rc) {
+               pr_err("Failed to decrypt swiotlb buffer (%d): disabling swiotlb!\n",
+                      rc);
+               mem->nslabs = 0;
+               return;
+       }
 
        mem->vaddr = swiotlb_mem_remap(mem, bytes);
        if (!mem->vaddr)
@@ -391,7 +399,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
        struct io_tlb_mem *mem = &io_tlb_default_mem;
        unsigned long nslabs = ALIGN(size >> IO_TLB_SHIFT, IO_TLB_SEGSIZE);
        unsigned char *vstart = NULL;
-       unsigned int order, area_order;
+       unsigned int order, area_order, slot_order;
        bool retried = false;
        int rc = 0;
 
@@ -442,19 +450,29 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
        if (!mem->areas)
                goto error_area;
 
+       slot_order = get_order(array_size(sizeof(*mem->slots), nslabs));
        mem->slots = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
-               get_order(array_size(sizeof(*mem->slots), nslabs)));
+               slot_order);
        if (!mem->slots)
                goto error_slots;
 
-       set_memory_decrypted((unsigned long)vstart,
-                            (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+       rc = set_memory_decrypted((unsigned long)vstart,
+                                 (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+       if (rc) {
+               pr_err("Failed to decrypt swiotlb buffer (%d): disabling swiotlb!\n",
+                      rc);
+               mem->nslabs = 0;
+               goto error_decrypted;
+       }
+
        swiotlb_init_io_tlb_mem(mem, virt_to_phys(vstart), nslabs, 0, true,
                                default_nareas);
 
        swiotlb_print_info();
        return 0;
 
+error_decrypted:
+       free_pages((unsigned long)mem->slots, slot_order);
 error_slots:
        free_pages((unsigned long)mem->areas, area_order);
 error_area:
@@ -986,6 +1004,7 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 
        /* Set Per-device io tlb area to one */
        unsigned int nareas = 1;
+       int rc = -ENOMEM;
 
        /*
         * Since multiple devices can share the same pool, the private data,
@@ -998,21 +1017,22 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
                        return -ENOMEM;
 
                mem->slots = kcalloc(nslabs, sizeof(*mem->slots), GFP_KERNEL);
-               if (!mem->slots) {
-                       kfree(mem);
-                       return -ENOMEM;
-               }
+               if (!mem->slots)
+                       goto free_mem;
 
                mem->areas = kcalloc(nareas, sizeof(*mem->areas),
                                GFP_KERNEL);
-               if (!mem->areas) {
-                       kfree(mem->slots);
-                       kfree(mem);
-                       return -ENOMEM;
+               if (!mem->areas)
+                       goto free_slots;
+
+               rc = set_memory_decrypted(
+                               (unsigned long)phys_to_virt(rmem->base),
+                               rmem->size >> PAGE_SHIFT);
+               if (rc) {
+                       pr_err("Failed to decrypt rmem buffer (%d)\n", rc);
+                       goto free_areas;
                }
 
-               set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
-                                    rmem->size >> PAGE_SHIFT);
                swiotlb_init_io_tlb_mem(mem, rmem->base, nslabs, SWIOTLB_FORCE,
                                        false, nareas);
                mem->for_alloc = true;
@@ -1025,6 +1045,14 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
        dev->dma_io_tlb_mem = mem;
 
        return 0;
+
+free_areas:
+       kfree(mem->areas);
+free_slots:
+       kfree(mem->slots);
+free_mem:
+       kfree(mem);
+       return rc;
 }




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux