Merge tag 'for-linus-5.6-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip

Pull xen updates from Juergen Gross:

 - fix a bug introduced in 5.5 in the Xen gntdev driver

 - fix the Xen balloon driver when running on ancient Xen versions

 - allow Xen stubdoms to control interrupt enable flags of
   passed-through PCI cards

 - release resources in Xen backends under memory pressure

* tag 'for-linus-5.6-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip:
  xen/blkback: Consistently insert one empty line between functions
  xen/blkback: Remove unnecessary static variable name prefixes
  xen/blkback: Squeeze page pools if a memory pressure is detected
  xenbus/backend: Protect xenbus callback with lock
  xenbus/backend: Add memory pressure handler callback
  xen/gntdev: Do not use mm notifiers with autotranslating guests
  xen/balloon: Support xend-based toolstack take two
  xen-pciback: optionally allow interrupt enable flag writes
This commit is contained in:
Linus Torvalds
2020-02-05 17:44:14 +00:00
16 changed files with 346 additions and 39 deletions

View File

@@ -1006,19 +1006,19 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
}
mutex_unlock(&priv->lock);
/*
* gntdev takes the address of the PTE in find_grant_ptes() and passes
* it to the hypervisor in gntdev_map_grant_pages(). The purpose of
* the notifier is to prevent the hypervisor pointer to the PTE from
* going stale.
*
* Since this vma's mappings can't be touched without the mmap_sem,
* and we are holding it now, there is no need for the notifier_range
* locking pattern.
*/
mmu_interval_read_begin(&map->notifier);
if (use_ptemod) {
/*
* gntdev takes the address of the PTE in find_grant_ptes() and
* passes it to the hypervisor in gntdev_map_grant_pages(). The
* purpose of the notifier is to prevent the hypervisor pointer
* to the PTE from going stale.
*
* Since this vma's mappings can't be touched without the
* mmap_sem, and we are holding it now, there is no need for
* the notifier_range locking pattern.
*/
mmu_interval_read_begin(&map->notifier);
map->pages_vm_start = vma->vm_start;
err = apply_to_page_range(vma->vm_mm, vma->vm_start,
vma->vm_end - vma->vm_start,

View File

@@ -94,7 +94,7 @@ static void watch_target(struct xenbus_watch *watch,
"%llu", &static_max) == 1))
static_max >>= PAGE_SHIFT - 10;
else
static_max = new_target;
static_max = balloon_stats.current_pages;
target_diff = (xen_pv_domain() || xen_initial_domain()) ? 0
: static_max - balloon_stats.target_pages;

View File

@@ -286,6 +286,43 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
return xen_pcibios_err_to_errno(err);
}
int xen_pcibk_get_interrupt_type(struct pci_dev *dev)
{
int err;
u16 val;
int ret = 0;
err = pci_read_config_word(dev, PCI_COMMAND, &val);
if (err)
return err;
if (!(val & PCI_COMMAND_INTX_DISABLE))
ret |= INTERRUPT_TYPE_INTX;
/*
* Do not trust dev->msi(x)_enabled here, as enabling could be done
* bypassing the pci_*msi* functions, by the qemu.
*/
if (dev->msi_cap) {
err = pci_read_config_word(dev,
dev->msi_cap + PCI_MSI_FLAGS,
&val);
if (err)
return err;
if (val & PCI_MSI_FLAGS_ENABLE)
ret |= INTERRUPT_TYPE_MSI;
}
if (dev->msix_cap) {
err = pci_read_config_word(dev,
dev->msix_cap + PCI_MSIX_FLAGS,
&val);
if (err)
return err;
if (val & PCI_MSIX_FLAGS_ENABLE)
ret |= INTERRUPT_TYPE_MSIX;
}
return ret;
}
void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev)
{
struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);

View File

@@ -65,6 +65,11 @@ struct config_field_entry {
void *data;
};
#define INTERRUPT_TYPE_NONE (1<<0)
#define INTERRUPT_TYPE_INTX (1<<1)
#define INTERRUPT_TYPE_MSI (1<<2)
#define INTERRUPT_TYPE_MSIX (1<<3)
extern bool xen_pcibk_permissive;
#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)
@@ -126,4 +131,6 @@ int xen_pcibk_config_capability_init(void);
int xen_pcibk_config_header_add_fields(struct pci_dev *dev);
int xen_pcibk_config_capability_add_fields(struct pci_dev *dev);
int xen_pcibk_get_interrupt_type(struct pci_dev *dev);
#endif /* __XEN_PCIBACK_CONF_SPACE_H__ */

View File

@@ -189,6 +189,85 @@ static const struct config_field caplist_pm[] = {
{}
};
static struct msi_msix_field_config {
u16 enable_bit; /* bit for enabling MSI/MSI-X */
unsigned int int_type; /* interrupt type for exclusiveness check */
} msi_field_config = {
.enable_bit = PCI_MSI_FLAGS_ENABLE,
.int_type = INTERRUPT_TYPE_MSI,
}, msix_field_config = {
.enable_bit = PCI_MSIX_FLAGS_ENABLE,
.int_type = INTERRUPT_TYPE_MSIX,
};
static void *msi_field_init(struct pci_dev *dev, int offset)
{
return &msi_field_config;
}
static void *msix_field_init(struct pci_dev *dev, int offset)
{
return &msix_field_config;
}
static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value,
void *data)
{
int err;
u16 old_value;
const struct msi_msix_field_config *field_config = data;
const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);
if (xen_pcibk_permissive || dev_data->permissive)
goto write;
err = pci_read_config_word(dev, offset, &old_value);
if (err)
return err;
if (new_value == old_value)
return 0;
if (!dev_data->allow_interrupt_control ||
(new_value ^ old_value) & ~field_config->enable_bit)
return PCIBIOS_SET_FAILED;
if (new_value & field_config->enable_bit) {
/* don't allow enabling together with other interrupt types */
int int_type = xen_pcibk_get_interrupt_type(dev);
if (int_type == INTERRUPT_TYPE_NONE ||
int_type == field_config->int_type)
goto write;
return PCIBIOS_SET_FAILED;
}
write:
return pci_write_config_word(dev, offset, new_value);
}
static const struct config_field caplist_msix[] = {
{
.offset = PCI_MSIX_FLAGS,
.size = 2,
.init = msix_field_init,
.u.w.read = xen_pcibk_read_config_word,
.u.w.write = msi_msix_flags_write,
},
{}
};
static const struct config_field caplist_msi[] = {
{
.offset = PCI_MSI_FLAGS,
.size = 2,
.init = msi_field_init,
.u.w.read = xen_pcibk_read_config_word,
.u.w.write = msi_msix_flags_write,
},
{}
};
static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = {
.capability = PCI_CAP_ID_PM,
.fields = caplist_pm,
@@ -197,11 +276,21 @@ static struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = {
.capability = PCI_CAP_ID_VPD,
.fields = caplist_vpd,
};
static struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = {
.capability = PCI_CAP_ID_MSI,
.fields = caplist_msi,
};
static struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = {
.capability = PCI_CAP_ID_MSIX,
.fields = caplist_msix,
};
int xen_pcibk_config_capability_init(void)
{
register_capability(&xen_pcibk_config_capability_vpd);
register_capability(&xen_pcibk_config_capability_pm);
register_capability(&xen_pcibk_config_capability_msi);
register_capability(&xen_pcibk_config_capability_msix);
return 0;
}

View File

@@ -117,6 +117,25 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
pci_clear_mwi(dev);
}
if (dev_data && dev_data->allow_interrupt_control) {
if ((cmd->val ^ value) & PCI_COMMAND_INTX_DISABLE) {
if (value & PCI_COMMAND_INTX_DISABLE) {
pci_intx(dev, 0);
} else {
/* Do not allow enabling INTx together with MSI or MSI-X. */
switch (xen_pcibk_get_interrupt_type(dev)) {
case INTERRUPT_TYPE_NONE:
pci_intx(dev, 1);
break;
case INTERRUPT_TYPE_INTX:
break;
default:
return PCIBIOS_SET_FAILED;
}
}
}
}
cmd->val = value;
if (!xen_pcibk_permissive && (!dev_data || !dev_data->permissive))

View File

@@ -304,6 +304,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
xen_pcibk_config_reset_dev(dev);
xen_pcibk_config_free_dyn_fields(dev);
dev_data->allow_interrupt_control = 0;
xen_unregister_device_domain_owner(dev);
spin_lock_irqsave(&found_psdev->lock, flags);
@@ -1431,6 +1433,65 @@ static ssize_t permissive_show(struct device_driver *drv, char *buf)
}
static DRIVER_ATTR_RW(permissive);
static ssize_t allow_interrupt_control_store(struct device_driver *drv,
const char *buf, size_t count)
{
int domain, bus, slot, func;
int err;
struct pcistub_device *psdev;
struct xen_pcibk_dev_data *dev_data;
err = str_to_slot(buf, &domain, &bus, &slot, &func);
if (err)
goto out;
psdev = pcistub_device_find(domain, bus, slot, func);
if (!psdev) {
err = -ENODEV;
goto out;
}
dev_data = pci_get_drvdata(psdev->dev);
/* the driver data for a device should never be null at this point */
if (!dev_data) {
err = -ENXIO;
goto release;
}
dev_data->allow_interrupt_control = 1;
release:
pcistub_device_put(psdev);
out:
if (!err)
err = count;
return err;
}
static ssize_t allow_interrupt_control_show(struct device_driver *drv,
char *buf)
{
struct pcistub_device *psdev;
struct xen_pcibk_dev_data *dev_data;
size_t count = 0;
unsigned long flags;
spin_lock_irqsave(&pcistub_devices_lock, flags);
list_for_each_entry(psdev, &pcistub_devices, dev_list) {
if (count >= PAGE_SIZE)
break;
if (!psdev->dev)
continue;
dev_data = pci_get_drvdata(psdev->dev);
if (!dev_data || !dev_data->allow_interrupt_control)
continue;
count +=
scnprintf(buf + count, PAGE_SIZE - count, "%s\n",
pci_name(psdev->dev));
}
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
return count;
}
static DRIVER_ATTR_RW(allow_interrupt_control);
static void pcistub_exit(void)
{
driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_new_slot);
@@ -1440,6 +1501,8 @@ static void pcistub_exit(void)
driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_quirks);
driver_remove_file(&xen_pcibk_pci_driver.driver,
&driver_attr_permissive);
driver_remove_file(&xen_pcibk_pci_driver.driver,
&driver_attr_allow_interrupt_control);
driver_remove_file(&xen_pcibk_pci_driver.driver,
&driver_attr_irq_handlers);
driver_remove_file(&xen_pcibk_pci_driver.driver,
@@ -1530,6 +1593,9 @@ static int __init pcistub_init(void)
if (!err)
err = driver_create_file(&xen_pcibk_pci_driver.driver,
&driver_attr_permissive);
if (!err)
err = driver_create_file(&xen_pcibk_pci_driver.driver,
&driver_attr_allow_interrupt_control);
if (!err)
err = driver_create_file(&xen_pcibk_pci_driver.driver,

View File

@@ -45,6 +45,7 @@ struct xen_pcibk_dev_data {
struct list_head config_fields;
struct pci_saved_state *pci_saved_state;
unsigned int permissive:1;
unsigned int allow_interrupt_control:1;
unsigned int warned_on_write:1;
unsigned int enable_intx:1;
unsigned int isr_on:1; /* Whether the IRQ handler is installed. */

View File

@@ -239,7 +239,9 @@ int xenbus_dev_probe(struct device *_dev)
goto fail;
}
spin_lock(&dev->reclaim_lock);
err = drv->probe(dev, id);
spin_unlock(&dev->reclaim_lock);
if (err)
goto fail_put;
@@ -268,8 +270,11 @@ int xenbus_dev_remove(struct device *_dev)
free_otherend_watch(dev);
if (drv->remove)
if (drv->remove) {
spin_lock(&dev->reclaim_lock);
drv->remove(dev);
spin_unlock(&dev->reclaim_lock);
}
module_put(drv->driver.owner);
@@ -468,6 +473,7 @@ int xenbus_probe_node(struct xen_bus_type *bus,
goto fail;
dev_set_name(&xendev->dev, "%s", devname);
spin_lock_init(&xendev->reclaim_lock);
/* Register with generic device framework. */
err = device_register(&xendev->dev);

View File

@@ -247,6 +247,41 @@ static int backend_probe_and_watch(struct notifier_block *notifier,
return NOTIFY_DONE;
}
static int backend_reclaim_memory(struct device *dev, void *data)
{
const struct xenbus_driver *drv;
struct xenbus_device *xdev;
if (!dev->driver)
return 0;
drv = to_xenbus_driver(dev->driver);
if (drv && drv->reclaim_memory) {
xdev = to_xenbus_device(dev);
if (!spin_trylock(&xdev->reclaim_lock))
return 0;
drv->reclaim_memory(xdev);
spin_unlock(&xdev->reclaim_lock);
}
return 0;
}
/*
* Returns 0 always because we are using shrinker to only detect memory
* pressure.
*/
static unsigned long backend_shrink_memory_count(struct shrinker *shrinker,
struct shrink_control *sc)
{
bus_for_each_dev(&xenbus_backend.bus, NULL, NULL,
backend_reclaim_memory);
return 0;
}
static struct shrinker backend_memory_shrinker = {
.count_objects = backend_shrink_memory_count,
.seeks = DEFAULT_SEEKS,
};
static int __init xenbus_probe_backend_init(void)
{
static struct notifier_block xenstore_notifier = {
@@ -263,6 +298,9 @@ static int __init xenbus_probe_backend_init(void)
register_xenstore_notifier(&xenstore_notifier);
if (register_shrinker(&backend_memory_shrinker))
pr_warn("shrinker registration failed\n");
return 0;
}
subsys_initcall(xenbus_probe_backend_init);