mask or unmask the adapter, as specified in mask
KVM_S390_IO_ADAPTER_MAP
- This is now a no-op. The mapping is purely done by the irq route.
+ Map an adapter indicator or summary page for long-term pinning so that
+ interrupt injection can be performed in atomic context. If long-term
+ pinning is not possible (e.g. file-backed memory), the page is verified
+ via a short-term pin and the ioctl returns success; interrupt injection
+ will use the non-atomic irqfd path with short-term pinning on each
+ interrupt. In Secure Execution mode this is a no-op and the ioctl
+ returns success.
+
KVM_S390_IO_ADAPTER_UNMAP
- This is now a no-op. The mapping is purely done by the irq route.
+ Unmap a previously mapped adapter indicator or summary page and release
+ the long-term pin. If the page was not long-term pinned (e.g. file-backed
+ memory), the map entry is removed and success is returned; if no prior
+ map entry exists, -ENOENT is returned. In Secure Execution mode this is
+ a no-op and the ioctl returns success.
KVM_DEV_FLIC_AISM
modify the adapter-interruption-suppression mode for a given isc if the
map->addr = host_addr;
map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM);
if (!map->page) {
- ret = -EINVAL;
- goto out;
+ /*
+ * Long-term pinning may fail for memory types such as file-backed
+ * memory. Verify that short-term pinning succeeds so that the
+ * non-atomic irqfd path can handle interrupt injection.
+ */
+ map->page = pin_map_page(kvm, host_addr, 0);
+ if (!map->page) {
+ ret = -EINVAL;
+ goto out;
+ }
+ unpin_user_page(map->page);
+ map->page = NULL;
+ map->pinned = false;
+ /* Add an entry to preserve MAP/UNMAP symmetry. */
+ } else {
+ map->pinned = true;
}
spin_lock_irqsave(&adapter->maps_lock, flags);
if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) {
ret = -EINVAL;
}
spin_unlock_irqrestore(&adapter->maps_lock, flags);
- if (ret)
+ if (ret && map->page)
unpin_user_page(map->page);
out:
if (ret)
struct s390_map_info *map, *tmp, *map_to_free;
struct page *map_page_to_put = NULL;
u64 map_addr_to_mark = 0;
+ bool map_pinned = false;
unsigned long flags;
int found = 0, idx;
list_del(&map->list);
map_page_to_put = map->page;
map_addr_to_mark = map->guest_addr;
+ map_pinned = map->pinned;
map_to_free = map;
break;
}
if (found) {
kfree(map_to_free);
- idx = srcu_read_lock(&kvm->srcu);
- mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT);
- set_page_dirty_lock(map_page_to_put);
- srcu_read_unlock(&kvm->srcu, idx);
- unpin_user_page(map_page_to_put);
+ if (map_pinned) {
+ /*
+ * Only long-term pinned pages need to be marked dirty
+ * and released. Fallback entries exist only for
+ * MAP/UNMAP symmetry.
+ */
+ idx = srcu_read_lock(&kvm->srcu);
+ mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT);
+ set_page_dirty_lock(map_page_to_put);
+ srcu_read_unlock(&kvm->srcu, idx);
+ unpin_user_page(map_page_to_put);
+ }
}
return found ? 0 : -ENOENT;
list_for_each_entry_safe(map, tmp, &local_list, list) {
list_del(&map->list);
- idx = srcu_read_lock(&kvm->srcu);
- mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT);
- set_page_dirty_lock(map->page);
- srcu_read_unlock(&kvm->srcu, idx);
- unpin_user_page(map->page);
+ if (map->pinned) {
+ idx = srcu_read_lock(&kvm->srcu);
+ mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT);
+ set_page_dirty_lock(map->page);
+ srcu_read_unlock(&kvm->srcu, idx);
+ unpin_user_page(map->page);
+ }
kfree(map);
}
}
return NULL;
list_for_each_entry(map, &adapter->maps, list) {
- if (map->addr == addr)
+ if (map->addr == addr) {
+ if (!map->pinned)
+ return NULL;
return map;
+ }
}
return NULL;
}