]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: s390: Enable adapter_indicators_set to use mapped pages
authorDouglas Freimuth <freimuth@linux.ibm.com>
Thu, 4 Jun 2026 19:27:54 +0000 (21:27 +0200)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Mon, 15 Jun 2026 12:18:33 +0000 (14:18 +0200)
The s390 adapter_indicators_set function can now be optimized to use
long-term mapped pages when available so that work can be
processed on a fast path when interrupts are disabled.
If adapter indicator pages are not mapped then local mapping is
done on a slow path as it is prior to this patch. For example, Secure
Execution environments will take the local mapping path as it does prior to
this patch.

Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Douglas Freimuth <freimuth@linux.ibm.com>
Acked-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260604192755.203143-3-freimuth@linux.ibm.com>

arch/s390/kvm/interrupt.c

index d11dea4cb0d3ea99d5fe13496417e8a4debf26ad..f276f76f814f4b887550c85e201d2d92c6ab8cfe 100644 (file)
@@ -2892,41 +2892,80 @@ static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap)
        return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit;
 }
 
+static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
+                                         u64 addr)
+{
+       struct s390_map_info *map;
+
+       if (!adapter)
+               return NULL;
+
+       list_for_each_entry(map, &adapter->maps, list) {
+               if (map->addr == addr)
+                       return map;
+       }
+       return NULL;
+}
+
 static int adapter_indicators_set(struct kvm *kvm,
                                  struct s390_io_adapter *adapter,
                                  struct kvm_s390_adapter_int *adapter_int)
 {
        unsigned long bit;
        int summary_set, idx;
-       struct page *ind_page, *summary_page;
+       struct s390_map_info *ind_info, *summary_info;
        void *map;
+       struct page *ind_page, *summary_page;
+       unsigned long flags;
 
-       ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0);
-       if (!ind_page)
-               return -1;
-       summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
-       if (!summary_page) {
+       ind_page = NULL;
+
+       spin_lock_irqsave(&adapter->maps_lock, flags);
+       ind_info = get_map_info(adapter, adapter_int->ind_addr);
+       if (!ind_info) {
+               spin_unlock_irqrestore(&adapter->maps_lock, flags);
+               ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0);
+               if (!ind_page)
+                       return -1;
+               idx = srcu_read_lock(&kvm->srcu);
+               map = page_address(ind_page);
+               bit = get_ind_bit(adapter_int->ind_addr,
+                                 adapter_int->ind_offset, adapter->swap);
+               set_bit(bit, map);
+               mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT);
+               set_page_dirty_lock(ind_page);
+               srcu_read_unlock(&kvm->srcu, idx);
                unpin_user_page(ind_page);
-               return -1;
+       } else {
+               map = page_address(ind_info->page);
+               bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
+               set_bit(bit, map);
+               spin_unlock_irqrestore(&adapter->maps_lock, flags);
+       }
+       spin_lock_irqsave(&adapter->maps_lock, flags);
+       summary_info = get_map_info(adapter, adapter_int->summary_addr);
+       if (!summary_info) {
+               spin_unlock_irqrestore(&adapter->maps_lock, flags);
+               summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
+               if (WARN_ON_ONCE(!summary_page))
+                       return -1;
+               idx = srcu_read_lock(&kvm->srcu);
+               map = page_address(summary_page);
+               bit = get_ind_bit(adapter_int->summary_addr,
+                                 adapter_int->summary_offset, adapter->swap);
+               summary_set = test_and_set_bit(bit, map);
+               mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT);
+               set_page_dirty_lock(summary_page);
+               srcu_read_unlock(&kvm->srcu, idx);
+               unpin_user_page(summary_page);
+       } else {
+               map = page_address(summary_info->page);
+               bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
+                                 adapter->swap);
+               summary_set = test_and_set_bit(bit, map);
+               spin_unlock_irqrestore(&adapter->maps_lock, flags);
        }
 
-       idx = srcu_read_lock(&kvm->srcu);
-       map = page_address(ind_page);
-       bit = get_ind_bit(adapter_int->ind_addr,
-                         adapter_int->ind_offset, adapter->swap);
-       set_bit(bit, map);
-       mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT);
-       set_page_dirty_lock(ind_page);
-       map = page_address(summary_page);
-       bit = get_ind_bit(adapter_int->summary_addr,
-                         adapter_int->summary_offset, adapter->swap);
-       summary_set = test_and_set_bit(bit, map);
-       mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT);
-       set_page_dirty_lock(summary_page);
-       srcu_read_unlock(&kvm->srcu, idx);
-
-       unpin_user_page(ind_page);
-       unpin_user_page(summary_page);
        return summary_set ? 0 : 1;
 }