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:
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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))
|
||||
|
@@ -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,
|
||||
|
@@ -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. */
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user