The S390 adapter_indicators_set function needs to be able to use mapped pages so that worked 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. Signed-off-by: Douglas Freimuth --- arch/s390/kvm/interrupt.c | 94 ++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index a9b418996225..12d8d38c260d 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2883,41 +2883,85 @@ 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) { - put_page(ind_page); - return -1; + raw_spin_lock_irqsave(&adapter->maps_lock, flags); + ind_info = get_map_info(adapter, adapter_int->ind_addr); + if (!ind_info) { + raw_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); + } else { + map = page_address(ind_info->page); + bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap); + set_bit(bit, map); + raw_spin_unlock_irqrestore(&adapter->maps_lock, flags); + } + raw_spin_lock_irqsave(&adapter->maps_lock, flags); + summary_info = get_map_info(adapter, adapter_int->summary_addr); + if (!summary_info) { + raw_spin_unlock_irqrestore(&adapter->maps_lock, flags); + summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0); + if (!summary_page) { + if (!ind_info) { + WARN_ON_ONCE(!ind_page); + unpin_user_page(ind_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); + } 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); + raw_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); + if (!ind_info) + unpin_user_page(ind_page); + if (!summary_info) + unpin_user_page(summary_page); return summary_set ? 0 : 1; } -- 2.52.0