xen: features and fixes for 4.1-rc0

- Use a single source list of hypercalls, generating other tables
   etc. at build time.
 - Add a "Xen PV" APIC driver to support >255 VCPUs in PV guests.
 - Significant performance improve to guest save/restore/migration.
 - scsiback/front save/restore support.
 - Infrastructure for multi-page xenbus rings.
 - Misc fixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQEcBAABAgAGBQJVL6OZAAoJEFxbo/MsZsTRs3YH/2AycBHs129DNnc2OLmOklBz
 AdD43k+FOfZlv0YU80WmPVmOpGGHGB5Pqkix2KtnvPYmtx3pb/5ikhDwSTWZpqBl
 Qq6/RgsRjYZ8VMKqrMTkJMrJWHQYbg8lgsP5810nsFBn/Qdbxms+WBqpMkFVo3b2
 rvUZj8QijMJPS3qr55DklVaOlXV4+sTAytTdCiubVnaB/agM2jjRflp/lnJrhtTg
 yc4NTrIlD1RsMV/lNh92upBP/pCm6Bs0zQ2H1v3hkdhBBmaO0IVXpSheYhfDOHfo
 9v209n137N7X86CGWImFk6m2b+EfiFnLFir07zKSA+iZwkYKn75znSdPfj0KCc0=
 =bxTm
 -----END PGP SIGNATURE-----

Merge tag 'stable/for-linus-4.1-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip

Pull xen features and fixes from David Vrabel:

 - use a single source list of hypercalls, generating other tables etc.
   at build time.

 - add a "Xen PV" APIC driver to support >255 VCPUs in PV guests.

 - significant performance improve to guest save/restore/migration.

 - scsiback/front save/restore support.

 - infrastructure for multi-page xenbus rings.

 - misc fixes.

* tag 'stable/for-linus-4.1-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip:
  xen/pci: Try harder to get PXM information for Xen
  xenbus_client: Extend interface to support multi-page ring
  xen-pciback: also support disabling of bus-mastering and memory-write-invalidate
  xen: support suspend/resume in pvscsi frontend
  xen: scsiback: add LUN of restored domain
  xen-scsiback: define a pr_fmt macro with xen-pvscsi
  xen/mce: fix up xen_late_init_mcelog() error handling
  xen/privcmd: improve performance of MMAPBATCH_V2
  xen: unify foreign GFN map/unmap for auto-xlated physmap guests
  x86/xen/apic: WARN with details.
  x86/xen: Provide a "Xen PV" APIC driver to support >255 VCPUs
  xen/pciback: Don't print scary messages when unsupported by hypervisor.
  xen: use generated hypercall symbols in arch/x86/xen/xen-head.S
  xen: use generated hypervisor symbols in arch/x86/xen/trace.c
  xen: synchronize include/xen/interface/xen.h with xen
  xen: build infrastructure for generating hypercall depending symbols
  xen: balloon: Use static attribute groups for sysfs entries
  xen: pcpu: Use static attribute groups for sysfs entry
This commit is contained in:
Linus Torvalds 2015-04-16 14:01:03 -05:00
commit 497a5df7bf
31 changed files with 1248 additions and 696 deletions

View File

@ -53,105 +53,33 @@ EXPORT_SYMBOL_GPL(xen_platform_pci_unplug);
static __read_mostly int xen_events_irq = -1; static __read_mostly int xen_events_irq = -1;
/* map fgmfn of domid to lpfn in the current domain */ int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn,
unsigned int domid)
{
int rc;
struct xen_add_to_physmap_range xatp = {
.domid = DOMID_SELF,
.foreign_domid = domid,
.size = 1,
.space = XENMAPSPACE_gmfn_foreign,
};
xen_ulong_t idx = fgmfn;
xen_pfn_t gpfn = lpfn;
int err = 0;
set_xen_guest_handle(xatp.idxs, &idx);
set_xen_guest_handle(xatp.gpfns, &gpfn);
set_xen_guest_handle(xatp.errs, &err);
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp);
if (rc || err) {
pr_warn("Failed to map pfn to mfn rc:%d:%d pfn:%lx mfn:%lx\n",
rc, err, lpfn, fgmfn);
return 1;
}
return 0;
}
struct remap_data {
xen_pfn_t fgmfn; /* foreign domain's gmfn */
pgprot_t prot;
domid_t domid;
struct vm_area_struct *vma;
int index;
struct page **pages;
struct xen_remap_mfn_info *info;
};
static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr,
void *data)
{
struct remap_data *info = data;
struct page *page = info->pages[info->index++];
unsigned long pfn = page_to_pfn(page);
pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot));
if (map_foreign_page(pfn, info->fgmfn, info->domid))
return -EFAULT;
set_pte_at(info->vma->vm_mm, addr, ptep, pte);
return 0;
}
int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
unsigned long addr, unsigned long addr,
xen_pfn_t mfn, int nr, xen_pfn_t *mfn, int nr,
pgprot_t prot, unsigned domid, int *err_ptr, pgprot_t prot,
unsigned domid,
struct page **pages) struct page **pages)
{ {
int err; return xen_xlate_remap_gfn_array(vma, addr, mfn, nr, err_ptr,
struct remap_data data; prot, domid, pages);
}
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
/* TBD: Batching, current sole caller only does page at a time */ /* Not used by XENFEAT_auto_translated guests. */
if (nr > 1) int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
return -EINVAL; unsigned long addr,
xen_pfn_t mfn, int nr,
data.fgmfn = mfn; pgprot_t prot, unsigned domid,
data.prot = prot; struct page **pages)
data.domid = domid; {
data.vma = vma; return -ENOSYS;
data.index = 0;
data.pages = pages;
err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT,
remap_pte_fn, &data);
return err;
} }
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range);
int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
int nr, struct page **pages) int nr, struct page **pages)
{ {
int i; return xen_xlate_unmap_gfn_range(vma, nr, pages);
for (i = 0; i < nr; i++) {
struct xen_remove_from_physmap xrp;
unsigned long rc, pfn;
pfn = page_to_pfn(pages[i]);
xrp.domid = DOMID_SELF;
xrp.gpfn = pfn;
rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp);
if (rc) {
pr_warn("Failed to unmap pfn:%lx rc:%ld\n",
pfn, rc);
return rc;
}
}
return 0;
} }
EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range); EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);

View File

@ -19,6 +19,9 @@ quiet_cmd_syshdr = SYSHDR $@
quiet_cmd_systbl = SYSTBL $@ quiet_cmd_systbl = SYSTBL $@
cmd_systbl = $(CONFIG_SHELL) '$(systbl)' $< $@ cmd_systbl = $(CONFIG_SHELL) '$(systbl)' $< $@
quiet_cmd_hypercalls = HYPERCALLS $@
cmd_hypercalls = $(CONFIG_SHELL) '$<' $@ $(filter-out $<,$^)
syshdr_abi_unistd_32 := i386 syshdr_abi_unistd_32 := i386
$(uapi)/unistd_32.h: $(syscall32) $(syshdr) $(uapi)/unistd_32.h: $(syscall32) $(syshdr)
$(call if_changed,syshdr) $(call if_changed,syshdr)
@ -47,10 +50,16 @@ $(out)/syscalls_32.h: $(syscall32) $(systbl)
$(out)/syscalls_64.h: $(syscall64) $(systbl) $(out)/syscalls_64.h: $(syscall64) $(systbl)
$(call if_changed,systbl) $(call if_changed,systbl)
$(out)/xen-hypercalls.h: $(srctree)/scripts/xen-hypercalls.sh
$(call if_changed,hypercalls)
$(out)/xen-hypercalls.h: $(srctree)/include/xen/interface/xen*.h
uapisyshdr-y += unistd_32.h unistd_64.h unistd_x32.h uapisyshdr-y += unistd_32.h unistd_64.h unistd_x32.h
syshdr-y += syscalls_32.h syshdr-y += syscalls_32.h
syshdr-$(CONFIG_X86_64) += unistd_32_ia32.h unistd_64_x32.h syshdr-$(CONFIG_X86_64) += unistd_32_ia32.h unistd_64_x32.h
syshdr-$(CONFIG_X86_64) += syscalls_64.h syshdr-$(CONFIG_X86_64) += syscalls_64.h
syshdr-$(CONFIG_XEN) += xen-hypercalls.h
targets += $(uapisyshdr-y) $(syshdr-y) targets += $(uapisyshdr-y) $(syshdr-y)

View File

@ -7,6 +7,7 @@
#include <xen/xen.h> #include <xen/xen.h>
#include <xen/interface/physdev.h> #include <xen/interface/physdev.h>
#include "xen-ops.h" #include "xen-ops.h"
#include "smp.h"
static unsigned int xen_io_apic_read(unsigned apic, unsigned reg) static unsigned int xen_io_apic_read(unsigned apic, unsigned reg)
{ {
@ -28,7 +29,186 @@ static unsigned int xen_io_apic_read(unsigned apic, unsigned reg)
return 0xfd; return 0xfd;
} }
static unsigned long xen_set_apic_id(unsigned int x)
{
WARN_ON(1);
return x;
}
static unsigned int xen_get_apic_id(unsigned long x)
{
return ((x)>>24) & 0xFFu;
}
static u32 xen_apic_read(u32 reg)
{
struct xen_platform_op op = {
.cmd = XENPF_get_cpuinfo,
.interface_version = XENPF_INTERFACE_VERSION,
.u.pcpu_info.xen_cpuid = 0,
};
int ret = 0;
/* Shouldn't need this as APIC is turned off for PV, and we only
* get called on the bootup processor. But just in case. */
if (!xen_initial_domain() || smp_processor_id())
return 0;
if (reg == APIC_LVR)
return 0x10;
#ifdef CONFIG_X86_32
if (reg == APIC_LDR)
return SET_APIC_LOGICAL_ID(1UL << smp_processor_id());
#endif
if (reg != APIC_ID)
return 0;
ret = HYPERVISOR_dom0_op(&op);
if (ret)
return 0;
return op.u.pcpu_info.apic_id << 24;
}
static void xen_apic_write(u32 reg, u32 val)
{
/* Warn to see if there's any stray references */
WARN(1,"register: %x, value: %x\n", reg, val);
}
static u64 xen_apic_icr_read(void)
{
return 0;
}
static void xen_apic_icr_write(u32 low, u32 id)
{
/* Warn to see if there's any stray references */
WARN_ON(1);
}
static u32 xen_safe_apic_wait_icr_idle(void)
{
return 0;
}
static int xen_apic_probe_pv(void)
{
if (xen_pv_domain())
return 1;
return 0;
}
static int xen_madt_oem_check(char *oem_id, char *oem_table_id)
{
return xen_pv_domain();
}
static int xen_id_always_valid(int apicid)
{
return 1;
}
static int xen_id_always_registered(void)
{
return 1;
}
static int xen_phys_pkg_id(int initial_apic_id, int index_msb)
{
return initial_apic_id >> index_msb;
}
#ifdef CONFIG_X86_32
static int xen_x86_32_early_logical_apicid(int cpu)
{
/* Match with APIC_LDR read. Otherwise setup_local_APIC complains. */
return 1 << cpu;
}
#endif
static void xen_noop(void)
{
}
static void xen_silent_inquire(int apicid)
{
}
static struct apic xen_pv_apic = {
.name = "Xen PV",
.probe = xen_apic_probe_pv,
.acpi_madt_oem_check = xen_madt_oem_check,
.apic_id_valid = xen_id_always_valid,
.apic_id_registered = xen_id_always_registered,
/* .irq_delivery_mode - used in native_compose_msi_msg only */
/* .irq_dest_mode - used in native_compose_msi_msg only */
.target_cpus = default_target_cpus,
.disable_esr = 0,
/* .dest_logical - default_send_IPI_ use it but we use our own. */
.check_apicid_used = default_check_apicid_used, /* Used on 32-bit */
.vector_allocation_domain = flat_vector_allocation_domain,
.init_apic_ldr = xen_noop, /* setup_local_APIC calls it */
.ioapic_phys_id_map = default_ioapic_phys_id_map, /* Used on 32-bit */
.setup_apic_routing = NULL,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.apicid_to_cpu_present = physid_set_mask_of_physid, /* Used on 32-bit */
.check_phys_apicid_present = default_check_phys_apicid_present, /* smp_sanity_check needs it */
.phys_pkg_id = xen_phys_pkg_id, /* detect_ht */
.get_apic_id = xen_get_apic_id,
.set_apic_id = xen_set_apic_id, /* Can be NULL on 32-bit. */
.apic_id_mask = 0xFF << 24, /* Used by verify_local_APIC. Match with what xen_get_apic_id does. */
.cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
#ifdef CONFIG_SMP
.send_IPI_mask = xen_send_IPI_mask,
.send_IPI_mask_allbutself = xen_send_IPI_mask_allbutself,
.send_IPI_allbutself = xen_send_IPI_allbutself,
.send_IPI_all = xen_send_IPI_all,
.send_IPI_self = xen_send_IPI_self,
#endif
/* .wait_for_init_deassert- used by AP bootup - smp_callin which we don't use */
.inquire_remote_apic = xen_silent_inquire,
.read = xen_apic_read,
.write = xen_apic_write,
.eoi_write = xen_apic_write,
.icr_read = xen_apic_icr_read,
.icr_write = xen_apic_icr_write,
.wait_icr_idle = xen_noop,
.safe_wait_icr_idle = xen_safe_apic_wait_icr_idle,
#ifdef CONFIG_X86_32
/* generic_processor_info and setup_local_APIC. */
.x86_32_early_logical_apicid = xen_x86_32_early_logical_apicid,
#endif
};
static void __init xen_apic_check(void)
{
if (apic == &xen_pv_apic)
return;
pr_info("Switched APIC routing from %s to %s.\n", apic->name,
xen_pv_apic.name);
apic = &xen_pv_apic;
}
void __init xen_init_apic(void) void __init xen_init_apic(void)
{ {
x86_io_apic_ops.read = xen_io_apic_read; x86_io_apic_ops.read = xen_io_apic_read;
/* On PV guests the APIC CPUID bit is disabled so none of the
* routines end up executing. */
if (!xen_initial_domain())
apic = &xen_pv_apic;
x86_platform.apic_post_init = xen_apic_check;
} }
apic_driver(xen_pv_apic);

View File

@ -928,92 +928,6 @@ static void xen_io_delay(void)
{ {
} }
#ifdef CONFIG_X86_LOCAL_APIC
static unsigned long xen_set_apic_id(unsigned int x)
{
WARN_ON(1);
return x;
}
static unsigned int xen_get_apic_id(unsigned long x)
{
return ((x)>>24) & 0xFFu;
}
static u32 xen_apic_read(u32 reg)
{
struct xen_platform_op op = {
.cmd = XENPF_get_cpuinfo,
.interface_version = XENPF_INTERFACE_VERSION,
.u.pcpu_info.xen_cpuid = 0,
};
int ret = 0;
/* Shouldn't need this as APIC is turned off for PV, and we only
* get called on the bootup processor. But just in case. */
if (!xen_initial_domain() || smp_processor_id())
return 0;
if (reg == APIC_LVR)
return 0x10;
if (reg != APIC_ID)
return 0;
ret = HYPERVISOR_dom0_op(&op);
if (ret)
return 0;
return op.u.pcpu_info.apic_id << 24;
}
static void xen_apic_write(u32 reg, u32 val)
{
/* Warn to see if there's any stray references */
WARN_ON(1);
}
static u64 xen_apic_icr_read(void)
{
return 0;
}
static void xen_apic_icr_write(u32 low, u32 id)
{
/* Warn to see if there's any stray references */
WARN_ON(1);
}
static void xen_apic_wait_icr_idle(void)
{
return;
}
static u32 xen_safe_apic_wait_icr_idle(void)
{
return 0;
}
static void set_xen_basic_apic_ops(void)
{
apic->read = xen_apic_read;
apic->write = xen_apic_write;
apic->icr_read = xen_apic_icr_read;
apic->icr_write = xen_apic_icr_write;
apic->wait_icr_idle = xen_apic_wait_icr_idle;
apic->safe_wait_icr_idle = xen_safe_apic_wait_icr_idle;
apic->set_apic_id = xen_set_apic_id;
apic->get_apic_id = xen_get_apic_id;
#ifdef CONFIG_SMP
apic->send_IPI_allbutself = xen_send_IPI_allbutself;
apic->send_IPI_mask_allbutself = xen_send_IPI_mask_allbutself;
apic->send_IPI_mask = xen_send_IPI_mask;
apic->send_IPI_all = xen_send_IPI_all;
apic->send_IPI_self = xen_send_IPI_self;
#endif
}
#endif
static void xen_clts(void) static void xen_clts(void)
{ {
struct multicall_space mcs; struct multicall_space mcs;
@ -1619,7 +1533,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
/* /*
* set up the basic apic ops. * set up the basic apic ops.
*/ */
set_xen_basic_apic_ops(); xen_init_apic();
#endif #endif
if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) { if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) {
@ -1732,8 +1646,6 @@ asmlinkage __visible void __init xen_start_kernel(void)
if (HYPERVISOR_dom0_op(&op) == 0) if (HYPERVISOR_dom0_op(&op) == 0)
boot_params.kbd_status = op.u.firmware_info.u.kbd_shift_flags; boot_params.kbd_status = op.u.firmware_info.u.kbd_shift_flags;
xen_init_apic();
/* Make sure ACS will be enabled */ /* Make sure ACS will be enabled */
pci_request_acs(); pci_request_acs();

View File

@ -2436,99 +2436,11 @@ void __init xen_hvm_init_mmu_ops(void)
} }
#endif #endif
#ifdef CONFIG_XEN_PVH
/*
* Map foreign gfn (fgfn), to local pfn (lpfn). This for the user
* space creating new guest on pvh dom0 and needing to map domU pages.
*/
static int xlate_add_to_p2m(unsigned long lpfn, unsigned long fgfn,
unsigned int domid)
{
int rc, err = 0;
xen_pfn_t gpfn = lpfn;
xen_ulong_t idx = fgfn;
struct xen_add_to_physmap_range xatp = {
.domid = DOMID_SELF,
.foreign_domid = domid,
.size = 1,
.space = XENMAPSPACE_gmfn_foreign,
};
set_xen_guest_handle(xatp.idxs, &idx);
set_xen_guest_handle(xatp.gpfns, &gpfn);
set_xen_guest_handle(xatp.errs, &err);
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp);
if (rc < 0)
return rc;
return err;
}
static int xlate_remove_from_p2m(unsigned long spfn, int count)
{
struct xen_remove_from_physmap xrp;
int i, rc;
for (i = 0; i < count; i++) {
xrp.domid = DOMID_SELF;
xrp.gpfn = spfn+i;
rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp);
if (rc)
break;
}
return rc;
}
struct xlate_remap_data {
unsigned long fgfn; /* foreign domain's gfn */
pgprot_t prot;
domid_t domid;
int index;
struct page **pages;
};
static int xlate_map_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr,
void *data)
{
int rc;
struct xlate_remap_data *remap = data;
unsigned long pfn = page_to_pfn(remap->pages[remap->index++]);
pte_t pteval = pte_mkspecial(pfn_pte(pfn, remap->prot));
rc = xlate_add_to_p2m(pfn, remap->fgfn, remap->domid);
if (rc)
return rc;
native_set_pte(ptep, pteval);
return 0;
}
static int xlate_remap_gfn_range(struct vm_area_struct *vma,
unsigned long addr, unsigned long mfn,
int nr, pgprot_t prot, unsigned domid,
struct page **pages)
{
int err;
struct xlate_remap_data pvhdata;
BUG_ON(!pages);
pvhdata.fgfn = mfn;
pvhdata.prot = prot;
pvhdata.domid = domid;
pvhdata.index = 0;
pvhdata.pages = pages;
err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT,
xlate_map_pte_fn, &pvhdata);
flush_tlb_all();
return err;
}
#endif
#define REMAP_BATCH_SIZE 16 #define REMAP_BATCH_SIZE 16
struct remap_data { struct remap_data {
unsigned long mfn; xen_pfn_t *mfn;
bool contiguous;
pgprot_t prot; pgprot_t prot;
struct mmu_update *mmu_update; struct mmu_update *mmu_update;
}; };
@ -2537,7 +2449,14 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token,
unsigned long addr, void *data) unsigned long addr, void *data)
{ {
struct remap_data *rmd = data; struct remap_data *rmd = data;
pte_t pte = pte_mkspecial(mfn_pte(rmd->mfn++, rmd->prot)); pte_t pte = pte_mkspecial(mfn_pte(*rmd->mfn, rmd->prot));
/* If we have a contigious range, just update the mfn itself,
else update pointer to be "next mfn". */
if (rmd->contiguous)
(*rmd->mfn)++;
else
rmd->mfn++;
rmd->mmu_update->ptr = virt_to_machine(ptep).maddr; rmd->mmu_update->ptr = virt_to_machine(ptep).maddr;
rmd->mmu_update->val = pte_val_ma(pte); rmd->mmu_update->val = pte_val_ma(pte);
@ -2546,26 +2465,26 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token,
return 0; return 0;
} }
int xen_remap_domain_mfn_range(struct vm_area_struct *vma, static int do_remap_mfn(struct vm_area_struct *vma,
unsigned long addr, unsigned long addr,
xen_pfn_t mfn, int nr, xen_pfn_t *mfn, int nr,
pgprot_t prot, unsigned domid, int *err_ptr, pgprot_t prot,
struct page **pages) unsigned domid,
struct page **pages)
{ {
int err = 0;
struct remap_data rmd; struct remap_data rmd;
struct mmu_update mmu_update[REMAP_BATCH_SIZE]; struct mmu_update mmu_update[REMAP_BATCH_SIZE];
int batch;
unsigned long range; unsigned long range;
int err = 0; int mapped = 0;
BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO)));
if (xen_feature(XENFEAT_auto_translated_physmap)) { if (xen_feature(XENFEAT_auto_translated_physmap)) {
#ifdef CONFIG_XEN_PVH #ifdef CONFIG_XEN_PVH
/* We need to update the local page tables and the xen HAP */ /* We need to update the local page tables and the xen HAP */
return xlate_remap_gfn_range(vma, addr, mfn, nr, prot, return xen_xlate_remap_gfn_array(vma, addr, mfn, nr, err_ptr,
domid, pages); prot, domid, pages);
#else #else
return -EINVAL; return -EINVAL;
#endif #endif
@ -2573,9 +2492,15 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
rmd.mfn = mfn; rmd.mfn = mfn;
rmd.prot = prot; rmd.prot = prot;
/* We use the err_ptr to indicate if there we are doing a contigious
* mapping or a discontigious mapping. */
rmd.contiguous = !err_ptr;
while (nr) { while (nr) {
batch = min(REMAP_BATCH_SIZE, nr); int index = 0;
int done = 0;
int batch = min(REMAP_BATCH_SIZE, nr);
int batch_left = batch;
range = (unsigned long)batch << PAGE_SHIFT; range = (unsigned long)batch << PAGE_SHIFT;
rmd.mmu_update = mmu_update; rmd.mmu_update = mmu_update;
@ -2584,23 +2509,72 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
if (err) if (err)
goto out; goto out;
err = HYPERVISOR_mmu_update(mmu_update, batch, NULL, domid); /* We record the error for each page that gives an error, but
if (err < 0) * continue mapping until the whole set is done */
goto out; do {
int i;
err = HYPERVISOR_mmu_update(&mmu_update[index],
batch_left, &done, domid);
/*
* @err_ptr may be the same buffer as @mfn, so
* only clear it after each chunk of @mfn is
* used.
*/
if (err_ptr) {
for (i = index; i < index + done; i++)
err_ptr[i] = 0;
}
if (err < 0) {
if (!err_ptr)
goto out;
err_ptr[i] = err;
done++; /* Skip failed frame. */
} else
mapped += done;
batch_left -= done;
index += done;
} while (batch_left);
nr -= batch; nr -= batch;
addr += range; addr += range;
if (err_ptr)
err_ptr += batch;
} }
err = 0;
out: out:
xen_flush_tlb_all(); xen_flush_tlb_all();
return err; return err < 0 ? err : mapped;
}
int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
unsigned long addr,
xen_pfn_t mfn, int nr,
pgprot_t prot, unsigned domid,
struct page **pages)
{
return do_remap_mfn(vma, addr, &mfn, nr, NULL, prot, domid, pages);
} }
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range);
int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
unsigned long addr,
xen_pfn_t *mfn, int nr,
int *err_ptr, pgprot_t prot,
unsigned domid, struct page **pages)
{
/* We BUG_ON because it's a programmer error to pass a NULL err_ptr,
* and the consequences later is quite hard to detect what the actual
* cause of "wrong memory was mapped in".
*/
BUG_ON(err_ptr == NULL);
return do_remap_mfn(vma, addr, mfn, nr, err_ptr, prot, domid, pages);
}
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array);
/* Returns: 0 success */ /* Returns: 0 success */
int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
int numpgs, struct page **pages) int numpgs, struct page **pages)
@ -2609,22 +2583,7 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
return 0; return 0;
#ifdef CONFIG_XEN_PVH #ifdef CONFIG_XEN_PVH
while (numpgs--) { return xen_xlate_unmap_gfn_range(vma, numpgs, pages);
/*
* The mmu has already cleaned up the process mmu
* resources at this point (lookup_address will return
* NULL).
*/
unsigned long pfn = page_to_pfn(pages[numpgs]);
xlate_remove_from_p2m(pfn, 1);
}
/*
* We don't need to flush tlbs because as part of
* xlate_remove_from_p2m, the hypervisor will do tlb flushes
* after removing the p2m entries from the EPT/NPT
*/
return 0;
#else #else
return -EINVAL; return -EINVAL;
#endif #endif

View File

@ -1,54 +1,12 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <xen/interface/xen.h> #include <xen/interface/xen.h>
#include <xen/interface/xen-mca.h>
#define N(x) [__HYPERVISOR_##x] = "("#x")" #define HYPERCALL(x) [__HYPERVISOR_##x] = "("#x")",
static const char *xen_hypercall_names[] = { static const char *xen_hypercall_names[] = {
N(set_trap_table), #include <asm/xen-hypercalls.h>
N(mmu_update),
N(set_gdt),
N(stack_switch),
N(set_callbacks),
N(fpu_taskswitch),
N(sched_op_compat),
N(dom0_op),
N(set_debugreg),
N(get_debugreg),
N(update_descriptor),
N(memory_op),
N(multicall),
N(update_va_mapping),
N(set_timer_op),
N(event_channel_op_compat),
N(xen_version),
N(console_io),
N(physdev_op_compat),
N(grant_table_op),
N(vm_assist),
N(update_va_mapping_otherdomain),
N(iret),
N(vcpu_op),
N(set_segment_base),
N(mmuext_op),
N(acm_op),
N(nmi_op),
N(sched_op),
N(callback_op),
N(xenoprof_op),
N(event_channel_op),
N(physdev_op),
N(hvm_op),
/* Architecture-specific hypercall definitions. */
N(arch_0),
N(arch_1),
N(arch_2),
N(arch_3),
N(arch_4),
N(arch_5),
N(arch_6),
N(arch_7),
}; };
#undef N #undef HYPERCALL
static const char *xen_hypercall_name(unsigned op) static const char *xen_hypercall_name(unsigned op)
{ {

View File

@ -12,6 +12,8 @@
#include <xen/interface/elfnote.h> #include <xen/interface/elfnote.h>
#include <xen/interface/features.h> #include <xen/interface/features.h>
#include <xen/interface/xen.h>
#include <xen/interface/xen-mca.h>
#include <asm/xen/interface.h> #include <asm/xen/interface.h>
#ifdef CONFIG_XEN_PVH #ifdef CONFIG_XEN_PVH
@ -85,59 +87,14 @@ ENTRY(xen_pvh_early_cpu_init)
.pushsection .text .pushsection .text
.balign PAGE_SIZE .balign PAGE_SIZE
ENTRY(hypercall_page) ENTRY(hypercall_page)
#define NEXT_HYPERCALL(x) \ .skip PAGE_SIZE
ENTRY(xen_hypercall_##x) \
.skip 32 #define HYPERCALL(n) \
.equ xen_hypercall_##n, hypercall_page + __HYPERVISOR_##n * 32; \
.type xen_hypercall_##n, @function; .size xen_hypercall_##n, 32
#include <asm/xen-hypercalls.h>
#undef HYPERCALL
NEXT_HYPERCALL(set_trap_table)
NEXT_HYPERCALL(mmu_update)
NEXT_HYPERCALL(set_gdt)
NEXT_HYPERCALL(stack_switch)
NEXT_HYPERCALL(set_callbacks)
NEXT_HYPERCALL(fpu_taskswitch)
NEXT_HYPERCALL(sched_op_compat)
NEXT_HYPERCALL(platform_op)
NEXT_HYPERCALL(set_debugreg)
NEXT_HYPERCALL(get_debugreg)
NEXT_HYPERCALL(update_descriptor)
NEXT_HYPERCALL(ni)
NEXT_HYPERCALL(memory_op)
NEXT_HYPERCALL(multicall)
NEXT_HYPERCALL(update_va_mapping)
NEXT_HYPERCALL(set_timer_op)
NEXT_HYPERCALL(event_channel_op_compat)
NEXT_HYPERCALL(xen_version)
NEXT_HYPERCALL(console_io)
NEXT_HYPERCALL(physdev_op_compat)
NEXT_HYPERCALL(grant_table_op)
NEXT_HYPERCALL(vm_assist)
NEXT_HYPERCALL(update_va_mapping_otherdomain)
NEXT_HYPERCALL(iret)
NEXT_HYPERCALL(vcpu_op)
NEXT_HYPERCALL(set_segment_base)
NEXT_HYPERCALL(mmuext_op)
NEXT_HYPERCALL(xsm_op)
NEXT_HYPERCALL(nmi_op)
NEXT_HYPERCALL(sched_op)
NEXT_HYPERCALL(callback_op)
NEXT_HYPERCALL(xenoprof_op)
NEXT_HYPERCALL(event_channel_op)
NEXT_HYPERCALL(physdev_op)
NEXT_HYPERCALL(hvm_op)
NEXT_HYPERCALL(sysctl)
NEXT_HYPERCALL(domctl)
NEXT_HYPERCALL(kexec_op)
NEXT_HYPERCALL(tmem_op) /* 38 */
ENTRY(xen_hypercall_rsvr)
.skip 320
NEXT_HYPERCALL(mca) /* 48 */
NEXT_HYPERCALL(arch_1)
NEXT_HYPERCALL(arch_2)
NEXT_HYPERCALL(arch_3)
NEXT_HYPERCALL(arch_4)
NEXT_HYPERCALL(arch_5)
NEXT_HYPERCALL(arch_6)
.balign PAGE_SIZE
.popsection .popsection
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux") ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux")

View File

@ -193,7 +193,7 @@ fail:
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page, static int xen_blkif_map(struct xen_blkif *blkif, grant_ref_t gref,
unsigned int evtchn) unsigned int evtchn)
{ {
int err; int err;
@ -202,7 +202,8 @@ static int xen_blkif_map(struct xen_blkif *blkif, unsigned long shared_page,
if (blkif->irq) if (blkif->irq)
return 0; return 0;
err = xenbus_map_ring_valloc(blkif->be->dev, shared_page, &blkif->blk_ring); err = xenbus_map_ring_valloc(blkif->be->dev, &gref, 1,
&blkif->blk_ring);
if (err < 0) if (err < 0)
return err; return err;

View File

@ -1245,6 +1245,7 @@ static int setup_blkring(struct xenbus_device *dev,
struct blkfront_info *info) struct blkfront_info *info)
{ {
struct blkif_sring *sring; struct blkif_sring *sring;
grant_ref_t gref;
int err; int err;
info->ring_ref = GRANT_INVALID_REF; info->ring_ref = GRANT_INVALID_REF;
@ -1257,13 +1258,13 @@ static int setup_blkring(struct xenbus_device *dev,
SHARED_RING_INIT(sring); SHARED_RING_INIT(sring);
FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring)); err = xenbus_grant_ring(dev, info->ring.sring, 1, &gref);
if (err < 0) { if (err < 0) {
free_page((unsigned long)sring); free_page((unsigned long)sring);
info->ring.sring = NULL; info->ring.sring = NULL;
goto fail; goto fail;
} }
info->ring_ref = err; info->ring_ref = gref;
err = xenbus_alloc_evtchn(dev, &info->evtchn); err = xenbus_alloc_evtchn(dev, &info->evtchn);
if (err) if (err)

View File

@ -193,6 +193,7 @@ static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
struct xenbus_transaction xbt; struct xenbus_transaction xbt;
const char *message = NULL; const char *message = NULL;
int rv; int rv;
grant_ref_t gref;
priv->shr = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO); priv->shr = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO);
if (!priv->shr) { if (!priv->shr) {
@ -200,11 +201,11 @@ static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
return -ENOMEM; return -ENOMEM;
} }
rv = xenbus_grant_ring(dev, virt_to_mfn(priv->shr)); rv = xenbus_grant_ring(dev, &priv->shr, 1, &gref);
if (rv < 0) if (rv < 0)
return rv; return rv;
priv->ring_ref = rv; priv->ring_ref = gref;
rv = xenbus_alloc_evtchn(dev, &priv->evtchn); rv = xenbus_alloc_evtchn(dev, &priv->evtchn);
if (rv) if (rv)

View File

@ -1780,7 +1780,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,
int err = -ENOMEM; int err = -ENOMEM;
err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
tx_ring_ref, &addr); &tx_ring_ref, 1, &addr);
if (err) if (err)
goto err; goto err;
@ -1788,7 +1788,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,
BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE); BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE);
err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
rx_ring_ref, &addr); &rx_ring_ref, 1, &addr);
if (err) if (err)
goto err; goto err;

View File

@ -1483,6 +1483,7 @@ static int setup_netfront(struct xenbus_device *dev,
{ {
struct xen_netif_tx_sring *txs; struct xen_netif_tx_sring *txs;
struct xen_netif_rx_sring *rxs; struct xen_netif_rx_sring *rxs;
grant_ref_t gref;
int err; int err;
queue->tx_ring_ref = GRANT_INVALID_REF; queue->tx_ring_ref = GRANT_INVALID_REF;
@ -1499,10 +1500,10 @@ static int setup_netfront(struct xenbus_device *dev,
SHARED_RING_INIT(txs); SHARED_RING_INIT(txs);
FRONT_RING_INIT(&queue->tx, txs, PAGE_SIZE); FRONT_RING_INIT(&queue->tx, txs, PAGE_SIZE);
err = xenbus_grant_ring(dev, virt_to_mfn(txs)); err = xenbus_grant_ring(dev, txs, 1, &gref);
if (err < 0) if (err < 0)
goto grant_tx_ring_fail; goto grant_tx_ring_fail;
queue->tx_ring_ref = err; queue->tx_ring_ref = gref;
rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH); rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
if (!rxs) { if (!rxs) {
@ -1513,10 +1514,10 @@ static int setup_netfront(struct xenbus_device *dev,
SHARED_RING_INIT(rxs); SHARED_RING_INIT(rxs);
FRONT_RING_INIT(&queue->rx, rxs, PAGE_SIZE); FRONT_RING_INIT(&queue->rx, rxs, PAGE_SIZE);
err = xenbus_grant_ring(dev, virt_to_mfn(rxs)); err = xenbus_grant_ring(dev, rxs, 1, &gref);
if (err < 0) if (err < 0)
goto grant_rx_ring_fail; goto grant_rx_ring_fail;
queue->rx_ring_ref = err; queue->rx_ring_ref = gref;
if (feature_split_evtchn) if (feature_split_evtchn)
err = setup_netfront_split(queue); err = setup_netfront_split(queue);

View File

@ -777,12 +777,13 @@ static int pcifront_publish_info(struct pcifront_device *pdev)
{ {
int err = 0; int err = 0;
struct xenbus_transaction trans; struct xenbus_transaction trans;
grant_ref_t gref;
err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info)); err = xenbus_grant_ring(pdev->xdev, pdev->sh_info, 1, &gref);
if (err < 0) if (err < 0)
goto out; goto out;
pdev->gnt_ref = err; pdev->gnt_ref = gref;
err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn); err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
if (err) if (err)

View File

@ -63,6 +63,7 @@
#define VSCSIFRONT_OP_ADD_LUN 1 #define VSCSIFRONT_OP_ADD_LUN 1
#define VSCSIFRONT_OP_DEL_LUN 2 #define VSCSIFRONT_OP_DEL_LUN 2
#define VSCSIFRONT_OP_READD_LUN 3
/* Tuning point. */ /* Tuning point. */
#define VSCSIIF_DEFAULT_CMD_PER_LUN 10 #define VSCSIIF_DEFAULT_CMD_PER_LUN 10
@ -113,8 +114,13 @@ struct vscsifrnt_info {
DECLARE_BITMAP(shadow_free_bitmap, VSCSIIF_MAX_REQS); DECLARE_BITMAP(shadow_free_bitmap, VSCSIIF_MAX_REQS);
struct vscsifrnt_shadow *shadow[VSCSIIF_MAX_REQS]; struct vscsifrnt_shadow *shadow[VSCSIIF_MAX_REQS];
/* Following items are protected by the host lock. */
wait_queue_head_t wq_sync; wait_queue_head_t wq_sync;
wait_queue_head_t wq_pause;
unsigned int wait_ring_available:1; unsigned int wait_ring_available:1;
unsigned int waiting_pause:1;
unsigned int pause:1;
unsigned callers;
char dev_state_path[64]; char dev_state_path[64];
struct task_struct *curr; struct task_struct *curr;
@ -274,31 +280,31 @@ static void scsifront_sync_cmd_done(struct vscsifrnt_info *info,
wake_up(&shadow->wq_reset); wake_up(&shadow->wq_reset);
} }
static int scsifront_cmd_done(struct vscsifrnt_info *info) static void scsifront_do_response(struct vscsifrnt_info *info,
struct vscsiif_response *ring_rsp)
{
if (WARN(ring_rsp->rqid >= VSCSIIF_MAX_REQS ||
test_bit(ring_rsp->rqid, info->shadow_free_bitmap),
"illegal rqid %u returned by backend!\n", ring_rsp->rqid))
return;
if (info->shadow[ring_rsp->rqid]->act == VSCSIIF_ACT_SCSI_CDB)
scsifront_cdb_cmd_done(info, ring_rsp);
else
scsifront_sync_cmd_done(info, ring_rsp);
}
static int scsifront_ring_drain(struct vscsifrnt_info *info)
{ {
struct vscsiif_response *ring_rsp; struct vscsiif_response *ring_rsp;
RING_IDX i, rp; RING_IDX i, rp;
int more_to_do = 0; int more_to_do = 0;
unsigned long flags;
spin_lock_irqsave(info->host->host_lock, flags);
rp = info->ring.sring->rsp_prod; rp = info->ring.sring->rsp_prod;
rmb(); /* ordering required respective to dom0 */ rmb(); /* ordering required respective to dom0 */
for (i = info->ring.rsp_cons; i != rp; i++) { for (i = info->ring.rsp_cons; i != rp; i++) {
ring_rsp = RING_GET_RESPONSE(&info->ring, i); ring_rsp = RING_GET_RESPONSE(&info->ring, i);
scsifront_do_response(info, ring_rsp);
if (WARN(ring_rsp->rqid >= VSCSIIF_MAX_REQS ||
test_bit(ring_rsp->rqid, info->shadow_free_bitmap),
"illegal rqid %u returned by backend!\n",
ring_rsp->rqid))
continue;
if (info->shadow[ring_rsp->rqid]->act == VSCSIIF_ACT_SCSI_CDB)
scsifront_cdb_cmd_done(info, ring_rsp);
else
scsifront_sync_cmd_done(info, ring_rsp);
} }
info->ring.rsp_cons = i; info->ring.rsp_cons = i;
@ -308,6 +314,18 @@ static int scsifront_cmd_done(struct vscsifrnt_info *info)
else else
info->ring.sring->rsp_event = i + 1; info->ring.sring->rsp_event = i + 1;
return more_to_do;
}
static int scsifront_cmd_done(struct vscsifrnt_info *info)
{
int more_to_do;
unsigned long flags;
spin_lock_irqsave(info->host->host_lock, flags);
more_to_do = scsifront_ring_drain(info);
info->wait_ring_available = 0; info->wait_ring_available = 0;
spin_unlock_irqrestore(info->host->host_lock, flags); spin_unlock_irqrestore(info->host->host_lock, flags);
@ -328,6 +346,24 @@ static irqreturn_t scsifront_irq_fn(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void scsifront_finish_all(struct vscsifrnt_info *info)
{
unsigned i;
struct vscsiif_response resp;
scsifront_ring_drain(info);
for (i = 0; i < VSCSIIF_MAX_REQS; i++) {
if (test_bit(i, info->shadow_free_bitmap))
continue;
resp.rqid = i;
resp.sense_len = 0;
resp.rslt = DID_RESET << 16;
resp.residual_len = 0;
scsifront_do_response(info, &resp);
}
}
static int map_data_for_request(struct vscsifrnt_info *info, static int map_data_for_request(struct vscsifrnt_info *info,
struct scsi_cmnd *sc, struct scsi_cmnd *sc,
struct vscsiif_request *ring_req, struct vscsiif_request *ring_req,
@ -475,6 +511,27 @@ static struct vscsiif_request *scsifront_command2ring(
return ring_req; return ring_req;
} }
static int scsifront_enter(struct vscsifrnt_info *info)
{
if (info->pause)
return 1;
info->callers++;
return 0;
}
static void scsifront_return(struct vscsifrnt_info *info)
{
info->callers--;
if (info->callers)
return;
if (!info->waiting_pause)
return;
info->waiting_pause = 0;
wake_up(&info->wq_pause);
}
static int scsifront_queuecommand(struct Scsi_Host *shost, static int scsifront_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *sc) struct scsi_cmnd *sc)
{ {
@ -486,6 +543,10 @@ static int scsifront_queuecommand(struct Scsi_Host *shost,
uint16_t rqid; uint16_t rqid;
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
if (scsifront_enter(info)) {
spin_unlock_irqrestore(shost->host_lock, flags);
return SCSI_MLQUEUE_HOST_BUSY;
}
if (RING_FULL(&info->ring)) if (RING_FULL(&info->ring))
goto busy; goto busy;
@ -505,6 +566,7 @@ static int scsifront_queuecommand(struct Scsi_Host *shost,
if (err < 0) { if (err < 0) {
pr_debug("%s: err %d\n", __func__, err); pr_debug("%s: err %d\n", __func__, err);
scsifront_put_rqid(info, rqid); scsifront_put_rqid(info, rqid);
scsifront_return(info);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
if (err == -ENOMEM) if (err == -ENOMEM)
return SCSI_MLQUEUE_HOST_BUSY; return SCSI_MLQUEUE_HOST_BUSY;
@ -514,11 +576,13 @@ static int scsifront_queuecommand(struct Scsi_Host *shost,
} }
scsifront_do_request(info); scsifront_do_request(info);
scsifront_return(info);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
return 0; return 0;
busy: busy:
scsifront_return(info);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
pr_debug("%s: busy\n", __func__); pr_debug("%s: busy\n", __func__);
return SCSI_MLQUEUE_HOST_BUSY; return SCSI_MLQUEUE_HOST_BUSY;
@ -549,7 +613,7 @@ static int scsifront_action_handler(struct scsi_cmnd *sc, uint8_t act)
if (ring_req) if (ring_req)
break; break;
} }
if (err) { if (err || info->pause) {
spin_unlock_irq(host->host_lock); spin_unlock_irq(host->host_lock);
kfree(shadow); kfree(shadow);
return FAILED; return FAILED;
@ -561,6 +625,11 @@ static int scsifront_action_handler(struct scsi_cmnd *sc, uint8_t act)
spin_lock_irq(host->host_lock); spin_lock_irq(host->host_lock);
} }
if (scsifront_enter(info)) {
spin_unlock_irq(host->host_lock);
return FAILED;
}
ring_req->act = act; ring_req->act = act;
ring_req->ref_rqid = s->rqid; ring_req->ref_rqid = s->rqid;
@ -587,6 +656,7 @@ static int scsifront_action_handler(struct scsi_cmnd *sc, uint8_t act)
err = FAILED; err = FAILED;
} }
scsifront_return(info);
spin_unlock_irq(host->host_lock); spin_unlock_irq(host->host_lock);
return err; return err;
} }
@ -644,6 +714,7 @@ static int scsifront_alloc_ring(struct vscsifrnt_info *info)
{ {
struct xenbus_device *dev = info->dev; struct xenbus_device *dev = info->dev;
struct vscsiif_sring *sring; struct vscsiif_sring *sring;
grant_ref_t gref;
int err = -ENOMEM; int err = -ENOMEM;
/***** Frontend to Backend ring start *****/ /***** Frontend to Backend ring start *****/
@ -656,14 +727,14 @@ static int scsifront_alloc_ring(struct vscsifrnt_info *info)
SHARED_RING_INIT(sring); SHARED_RING_INIT(sring);
FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
err = xenbus_grant_ring(dev, virt_to_mfn(sring)); err = xenbus_grant_ring(dev, sring, 1, &gref);
if (err < 0) { if (err < 0) {
free_page((unsigned long)sring); free_page((unsigned long)sring);
xenbus_dev_fatal(dev, err, xenbus_dev_fatal(dev, err,
"fail to grant shared ring (Front to Back)"); "fail to grant shared ring (Front to Back)");
return err; return err;
} }
info->ring_ref = err; info->ring_ref = gref;
err = xenbus_alloc_evtchn(dev, &info->evtchn); err = xenbus_alloc_evtchn(dev, &info->evtchn);
if (err) { if (err) {
@ -698,6 +769,13 @@ free_gnttab:
return err; return err;
} }
static void scsifront_free_ring(struct vscsifrnt_info *info)
{
unbind_from_irqhandler(info->irq, info);
gnttab_end_foreign_access(info->ring_ref, 0,
(unsigned long)info->ring.sring);
}
static int scsifront_init_ring(struct vscsifrnt_info *info) static int scsifront_init_ring(struct vscsifrnt_info *info)
{ {
struct xenbus_device *dev = info->dev; struct xenbus_device *dev = info->dev;
@ -744,9 +822,7 @@ again:
fail: fail:
xenbus_transaction_end(xbt, 1); xenbus_transaction_end(xbt, 1);
free_sring: free_sring:
unbind_from_irqhandler(info->irq, info); scsifront_free_ring(info);
gnttab_end_foreign_access(info->ring_ref, 0,
(unsigned long)info->ring.sring);
return err; return err;
} }
@ -779,6 +855,7 @@ static int scsifront_probe(struct xenbus_device *dev,
} }
init_waitqueue_head(&info->wq_sync); init_waitqueue_head(&info->wq_sync);
init_waitqueue_head(&info->wq_pause);
spin_lock_init(&info->shadow_lock); spin_lock_init(&info->shadow_lock);
snprintf(name, TASK_COMM_LEN, "vscsiif.%d", host->host_no); snprintf(name, TASK_COMM_LEN, "vscsiif.%d", host->host_no);
@ -802,13 +879,60 @@ static int scsifront_probe(struct xenbus_device *dev,
return 0; return 0;
free_sring: free_sring:
unbind_from_irqhandler(info->irq, info); scsifront_free_ring(info);
gnttab_end_foreign_access(info->ring_ref, 0,
(unsigned long)info->ring.sring);
scsi_host_put(host); scsi_host_put(host);
return err; return err;
} }
static int scsifront_resume(struct xenbus_device *dev)
{
struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
struct Scsi_Host *host = info->host;
int err;
spin_lock_irq(host->host_lock);
/* Finish all still pending commands. */
scsifront_finish_all(info);
spin_unlock_irq(host->host_lock);
/* Reconnect to dom0. */
scsifront_free_ring(info);
err = scsifront_init_ring(info);
if (err) {
dev_err(&dev->dev, "fail to resume %d\n", err);
scsi_host_put(host);
return err;
}
xenbus_switch_state(dev, XenbusStateInitialised);
return 0;
}
static int scsifront_suspend(struct xenbus_device *dev)
{
struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
struct Scsi_Host *host = info->host;
int err = 0;
/* No new commands for the backend. */
spin_lock_irq(host->host_lock);
info->pause = 1;
while (info->callers && !err) {
info->waiting_pause = 1;
info->wait_ring_available = 0;
spin_unlock_irq(host->host_lock);
wake_up(&info->wq_sync);
err = wait_event_interruptible(info->wq_pause,
!info->waiting_pause);
spin_lock_irq(host->host_lock);
}
spin_unlock_irq(host->host_lock);
return err;
}
static int scsifront_remove(struct xenbus_device *dev) static int scsifront_remove(struct xenbus_device *dev)
{ {
struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev); struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
@ -823,10 +947,7 @@ static int scsifront_remove(struct xenbus_device *dev)
} }
mutex_unlock(&scsifront_mutex); mutex_unlock(&scsifront_mutex);
gnttab_end_foreign_access(info->ring_ref, 0, scsifront_free_ring(info);
(unsigned long)info->ring.sring);
unbind_from_irqhandler(info->irq, info);
scsi_host_put(info->host); scsi_host_put(info->host);
return 0; return 0;
@ -919,6 +1040,12 @@ static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
scsi_device_put(sdev); scsi_device_put(sdev);
} }
break; break;
case VSCSIFRONT_OP_READD_LUN:
if (device_state == XenbusStateConnected)
xenbus_printf(XBT_NIL, dev->nodename,
info->dev_state_path,
"%d", XenbusStateConnected);
break;
default: default:
break; break;
} }
@ -932,21 +1059,29 @@ static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
static void scsifront_read_backend_params(struct xenbus_device *dev, static void scsifront_read_backend_params(struct xenbus_device *dev,
struct vscsifrnt_info *info) struct vscsifrnt_info *info)
{ {
unsigned int sg_grant; unsigned int sg_grant, nr_segs;
int ret; int ret;
struct Scsi_Host *host = info->host; struct Scsi_Host *host = info->host;
ret = xenbus_scanf(XBT_NIL, dev->otherend, "feature-sg-grant", "%u", ret = xenbus_scanf(XBT_NIL, dev->otherend, "feature-sg-grant", "%u",
&sg_grant); &sg_grant);
if (ret == 1 && sg_grant) { if (ret != 1)
sg_grant = min_t(unsigned int, sg_grant, SG_ALL); sg_grant = 0;
sg_grant = max_t(unsigned int, sg_grant, VSCSIIF_SG_TABLESIZE); nr_segs = min_t(unsigned int, sg_grant, SG_ALL);
host->sg_tablesize = min_t(unsigned int, sg_grant, nr_segs = max_t(unsigned int, nr_segs, VSCSIIF_SG_TABLESIZE);
nr_segs = min_t(unsigned int, nr_segs,
VSCSIIF_SG_TABLESIZE * PAGE_SIZE / VSCSIIF_SG_TABLESIZE * PAGE_SIZE /
sizeof(struct scsiif_request_segment)); sizeof(struct scsiif_request_segment));
host->max_sectors = (host->sg_tablesize - 1) * PAGE_SIZE / 512;
} if (!info->pause && sg_grant)
dev_info(&dev->dev, "using up to %d SG entries\n", host->sg_tablesize); dev_info(&dev->dev, "using up to %d SG entries\n", nr_segs);
else if (info->pause && nr_segs < host->sg_tablesize)
dev_warn(&dev->dev,
"SG entries decreased from %d to %u - device may not work properly anymore\n",
host->sg_tablesize, nr_segs);
host->sg_tablesize = nr_segs;
host->max_sectors = (nr_segs - 1) * PAGE_SIZE / 512;
} }
static void scsifront_backend_changed(struct xenbus_device *dev, static void scsifront_backend_changed(struct xenbus_device *dev,
@ -965,6 +1100,14 @@ static void scsifront_backend_changed(struct xenbus_device *dev,
case XenbusStateConnected: case XenbusStateConnected:
scsifront_read_backend_params(dev, info); scsifront_read_backend_params(dev, info);
if (info->pause) {
scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_READD_LUN);
xenbus_switch_state(dev, XenbusStateConnected);
info->pause = 0;
return;
}
if (xenbus_read_driver_state(dev->nodename) == if (xenbus_read_driver_state(dev->nodename) ==
XenbusStateInitialised) XenbusStateInitialised)
scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
@ -1002,6 +1145,8 @@ static struct xenbus_driver scsifront_driver = {
.ids = scsifront_ids, .ids = scsifront_ids,
.probe = scsifront_probe, .probe = scsifront_probe,
.remove = scsifront_remove, .remove = scsifront_remove,
.resume = scsifront_resume,
.suspend = scsifront_suspend,
.otherend_changed = scsifront_backend_changed, .otherend_changed = scsifront_backend_changed,
}; };

View File

@ -270,4 +270,10 @@ config XEN_EFI
def_bool y def_bool y
depends on X86_64 && EFI depends on X86_64 && EFI
config XEN_AUTO_XLATE
def_bool y
depends on ARM || ARM64 || XEN_PVHVM
help
Support for auto-translated physmap guests.
endmenu endmenu

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU) += xen-acpi-cpuhotplug.o
obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o
obj-$(CONFIG_XEN_EFI) += efi.o obj-$(CONFIG_XEN_EFI) += efi.o
obj-$(CONFIG_XEN_SCSI_BACKEND) += xen-scsiback.o obj-$(CONFIG_XEN_SCSI_BACKEND) += xen-scsiback.o
obj-$(CONFIG_XEN_AUTO_XLATE) += xlate_mmu.o
xen-evtchn-y := evtchn.o xen-evtchn-y := evtchn.o
xen-gntdev-y := gntdev.o xen-gntdev-y := gntdev.o
xen-gntalloc-y := gntalloc.o xen-gntalloc-y := gntalloc.o

View File

@ -393,14 +393,25 @@ static int bind_virq_for_mce(void)
static int __init xen_late_init_mcelog(void) static int __init xen_late_init_mcelog(void)
{ {
/* Only DOM0 is responsible for MCE logging */ int ret;
if (xen_initial_domain()) {
/* register character device /dev/mcelog for xen mcelog */
if (misc_register(&xen_mce_chrdev_device))
return -ENODEV;
return bind_virq_for_mce();
}
return -ENODEV; /* Only DOM0 is responsible for MCE logging */
if (!xen_initial_domain())
return -ENODEV;
/* register character device /dev/mcelog for xen mcelog */
ret = misc_register(&xen_mce_chrdev_device);
if (ret)
return ret;
ret = bind_virq_for_mce();
if (ret)
goto deregister;
return 0;
deregister:
misc_deregister(&xen_mce_chrdev_device);
return ret;
} }
device_initcall(xen_late_init_mcelog); device_initcall(xen_late_init_mcelog);

View File

@ -19,6 +19,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/pci-acpi.h>
#include <xen/xen.h> #include <xen/xen.h>
#include <xen/interface/physdev.h> #include <xen/interface/physdev.h>
#include <xen/interface/xen.h> #include <xen/interface/xen.h>
@ -67,12 +68,22 @@ static int xen_add_device(struct device *dev)
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
handle = ACPI_HANDLE(&pci_dev->dev); handle = ACPI_HANDLE(&pci_dev->dev);
if (!handle && pci_dev->bus->bridge)
handle = ACPI_HANDLE(pci_dev->bus->bridge);
#ifdef CONFIG_PCI_IOV #ifdef CONFIG_PCI_IOV
if (!handle && pci_dev->is_virtfn) if (!handle && pci_dev->is_virtfn)
handle = ACPI_HANDLE(physfn->bus->bridge); handle = ACPI_HANDLE(physfn->bus->bridge);
#endif #endif
if (!handle) {
/*
* This device was not listed in the ACPI name space at
* all. Try to get acpi handle of parent pci bus.
*/
struct pci_bus *pbus;
for (pbus = pci_dev->bus; pbus; pbus = pbus->parent) {
handle = acpi_pci_get_bridge_handle(pbus);
if (handle)
break;
}
}
if (handle) { if (handle) {
acpi_status status; acpi_status status;

View File

@ -132,6 +132,33 @@ static ssize_t __ref store_online(struct device *dev,
} }
static DEVICE_ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); static DEVICE_ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online);
static struct attribute *pcpu_dev_attrs[] = {
&dev_attr_online.attr,
NULL
};
static umode_t pcpu_dev_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
/*
* Xen never offline cpu0 due to several restrictions
* and assumptions. This basically doesn't add a sys control
* to user, one cannot attempt to offline BSP.
*/
return dev->id ? attr->mode : 0;
}
static const struct attribute_group pcpu_dev_group = {
.attrs = pcpu_dev_attrs,
.is_visible = pcpu_dev_is_visible,
};
static const struct attribute_group *pcpu_dev_groups[] = {
&pcpu_dev_group,
NULL
};
static bool xen_pcpu_online(uint32_t flags) static bool xen_pcpu_online(uint32_t flags)
{ {
return !!(flags & XEN_PCPU_FLAGS_ONLINE); return !!(flags & XEN_PCPU_FLAGS_ONLINE);
@ -181,9 +208,6 @@ static void unregister_and_remove_pcpu(struct pcpu *pcpu)
return; return;
dev = &pcpu->dev; dev = &pcpu->dev;
if (dev->id)
device_remove_file(dev, &dev_attr_online);
/* pcpu remove would be implicitly done */ /* pcpu remove would be implicitly done */
device_unregister(dev); device_unregister(dev);
} }
@ -200,6 +224,7 @@ static int register_pcpu(struct pcpu *pcpu)
dev->bus = &xen_pcpu_subsys; dev->bus = &xen_pcpu_subsys;
dev->id = pcpu->cpu_id; dev->id = pcpu->cpu_id;
dev->release = pcpu_release; dev->release = pcpu_release;
dev->groups = pcpu_dev_groups;
err = device_register(dev); err = device_register(dev);
if (err) { if (err) {
@ -207,19 +232,6 @@ static int register_pcpu(struct pcpu *pcpu)
return err; return err;
} }
/*
* Xen never offline cpu0 due to several restrictions
* and assumptions. This basically doesn't add a sys control
* to user, one cannot attempt to offline BSP.
*/
if (dev->id) {
err = device_create_file(dev, &dev_attr_online);
if (err) {
device_unregister(dev);
return err;
}
}
return 0; return 0;
} }

View File

@ -159,6 +159,40 @@ static int traverse_pages(unsigned nelem, size_t size,
return ret; return ret;
} }
/*
* Similar to traverse_pages, but use each page as a "block" of
* data to be processed as one unit.
*/
static int traverse_pages_block(unsigned nelem, size_t size,
struct list_head *pos,
int (*fn)(void *data, int nr, void *state),
void *state)
{
void *pagedata;
unsigned pageidx;
int ret = 0;
BUG_ON(size > PAGE_SIZE);
pageidx = PAGE_SIZE;
while (nelem) {
int nr = (PAGE_SIZE/size);
struct page *page;
if (nr > nelem)
nr = nelem;
pos = pos->next;
page = list_entry(pos, struct page, lru);
pagedata = page_address(page);
ret = (*fn)(pagedata, nr, state);
if (ret)
break;
nelem -= nr;
}
return ret;
}
struct mmap_mfn_state { struct mmap_mfn_state {
unsigned long va; unsigned long va;
struct vm_area_struct *vma; struct vm_area_struct *vma;
@ -274,39 +308,25 @@ struct mmap_batch_state {
/* auto translated dom0 note: if domU being created is PV, then mfn is /* auto translated dom0 note: if domU being created is PV, then mfn is
* mfn(addr on bus). If it's auto xlated, then mfn is pfn (input to HAP). * mfn(addr on bus). If it's auto xlated, then mfn is pfn (input to HAP).
*/ */
static int mmap_batch_fn(void *data, void *state) static int mmap_batch_fn(void *data, int nr, void *state)
{ {
xen_pfn_t *mfnp = data; xen_pfn_t *mfnp = data;
struct mmap_batch_state *st = state; struct mmap_batch_state *st = state;
struct vm_area_struct *vma = st->vma; struct vm_area_struct *vma = st->vma;
struct page **pages = vma->vm_private_data; struct page **pages = vma->vm_private_data;
struct page *cur_page = NULL; struct page **cur_pages = NULL;
int ret; int ret;
if (xen_feature(XENFEAT_auto_translated_physmap)) if (xen_feature(XENFEAT_auto_translated_physmap))
cur_page = pages[st->index++]; cur_pages = &pages[st->index];
ret = xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1, BUG_ON(nr < 0);
st->vma->vm_page_prot, st->domain, ret = xen_remap_domain_mfn_array(st->vma, st->va & PAGE_MASK, mfnp, nr,
&cur_page); (int *)mfnp, st->vma->vm_page_prot,
st->domain, cur_pages);
/* Store error code for second pass. */ /* Adjust the global_error? */
if (st->version == 1) { if (ret != nr) {
if (ret < 0) {
/*
* V1 encodes the error codes in the 32bit top nibble of the
* mfn (with its known limitations vis-a-vis 64 bit callers).
*/
*mfnp |= (ret == -ENOENT) ?
PRIVCMD_MMAPBATCH_PAGED_ERROR :
PRIVCMD_MMAPBATCH_MFN_ERROR;
}
} else { /* st->version == 2 */
*((int *) mfnp) = ret;
}
/* And see if it affects the global_error. */
if (ret < 0) {
if (ret == -ENOENT) if (ret == -ENOENT)
st->global_error = -ENOENT; st->global_error = -ENOENT;
else { else {
@ -315,23 +335,35 @@ static int mmap_batch_fn(void *data, void *state)
st->global_error = 1; st->global_error = 1;
} }
} }
st->va += PAGE_SIZE; st->va += PAGE_SIZE * nr;
st->index += nr;
return 0; return 0;
} }
static int mmap_return_errors(void *data, void *state) static int mmap_return_error(int err, struct mmap_batch_state *st)
{ {
struct mmap_batch_state *st = state; int ret;
if (st->version == 1) { if (st->version == 1) {
xen_pfn_t mfnp = *((xen_pfn_t *) data); if (err) {
if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) xen_pfn_t mfn;
return __put_user(mfnp, st->user_mfn++);
else ret = get_user(mfn, st->user_mfn);
if (ret < 0)
return ret;
/*
* V1 encodes the error codes in the 32bit top
* nibble of the mfn (with its known
* limitations vis-a-vis 64 bit callers).
*/
mfn |= (err == -ENOENT) ?
PRIVCMD_MMAPBATCH_PAGED_ERROR :
PRIVCMD_MMAPBATCH_MFN_ERROR;
return __put_user(mfn, st->user_mfn++);
} else
st->user_mfn++; st->user_mfn++;
} else { /* st->version == 2 */ } else { /* st->version == 2 */
int err = *((int *) data);
if (err) if (err)
return __put_user(err, st->user_err++); return __put_user(err, st->user_err++);
else else
@ -341,6 +373,21 @@ static int mmap_return_errors(void *data, void *state)
return 0; return 0;
} }
static int mmap_return_errors(void *data, int nr, void *state)
{
struct mmap_batch_state *st = state;
int *errs = data;
int i;
int ret;
for (i = 0; i < nr; i++) {
ret = mmap_return_error(errs[i], st);
if (ret < 0)
return ret;
}
return 0;
}
/* Allocate pfns that are then mapped with gmfns from foreign domid. Update /* Allocate pfns that are then mapped with gmfns from foreign domid. Update
* the vma with the page info to use later. * the vma with the page info to use later.
* Returns: 0 if success, otherwise -errno * Returns: 0 if success, otherwise -errno
@ -472,8 +519,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
state.version = version; state.version = version;
/* mmap_batch_fn guarantees ret == 0 */ /* mmap_batch_fn guarantees ret == 0 */
BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t),
&pagelist, mmap_batch_fn, &state)); &pagelist, mmap_batch_fn, &state));
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
@ -481,8 +528,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
/* Write back errors in second pass. */ /* Write back errors in second pass. */
state.user_mfn = (xen_pfn_t *)m.arr; state.user_mfn = (xen_pfn_t *)m.arr;
state.user_err = m.err; state.user_err = m.err;
ret = traverse_pages(m.num, sizeof(xen_pfn_t), ret = traverse_pages_block(m.num, sizeof(xen_pfn_t),
&pagelist, mmap_return_errors, &state); &pagelist, mmap_return_errors, &state);
} else } else
ret = 0; ret = 0;

View File

@ -193,13 +193,18 @@ static DEVICE_ATTR(target, S_IRUGO | S_IWUSR,
show_target, store_target); show_target, store_target);
static struct device_attribute *balloon_attrs[] = { static struct attribute *balloon_attrs[] = {
&dev_attr_target_kb, &dev_attr_target_kb.attr,
&dev_attr_target, &dev_attr_target.attr,
&dev_attr_schedule_delay.attr, &dev_attr_schedule_delay.attr.attr,
&dev_attr_max_schedule_delay.attr, &dev_attr_max_schedule_delay.attr.attr,
&dev_attr_retry_count.attr, &dev_attr_retry_count.attr.attr,
&dev_attr_max_retry_count.attr &dev_attr_max_retry_count.attr.attr,
NULL
};
static const struct attribute_group balloon_group = {
.attrs = balloon_attrs
}; };
static struct attribute *balloon_info_attrs[] = { static struct attribute *balloon_info_attrs[] = {
@ -214,6 +219,12 @@ static const struct attribute_group balloon_info_group = {
.attrs = balloon_info_attrs .attrs = balloon_info_attrs
}; };
static const struct attribute_group *balloon_groups[] = {
&balloon_group,
&balloon_info_group,
NULL
};
static struct bus_type balloon_subsys = { static struct bus_type balloon_subsys = {
.name = BALLOON_CLASS_NAME, .name = BALLOON_CLASS_NAME,
.dev_name = BALLOON_CLASS_NAME, .dev_name = BALLOON_CLASS_NAME,
@ -221,7 +232,7 @@ static struct bus_type balloon_subsys = {
static int register_balloon(struct device *dev) static int register_balloon(struct device *dev)
{ {
int i, error; int error;
error = subsys_system_register(&balloon_subsys, NULL); error = subsys_system_register(&balloon_subsys, NULL);
if (error) if (error)
@ -229,6 +240,7 @@ static int register_balloon(struct device *dev)
dev->id = 0; dev->id = 0;
dev->bus = &balloon_subsys; dev->bus = &balloon_subsys;
dev->groups = balloon_groups;
error = device_register(dev); error = device_register(dev);
if (error) { if (error) {
@ -236,24 +248,7 @@ static int register_balloon(struct device *dev)
return error; return error;
} }
for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) {
error = device_create_file(dev, balloon_attrs[i]);
if (error)
goto fail;
}
error = sysfs_create_group(&dev->kobj, &balloon_info_group);
if (error)
goto fail;
return 0; return 0;
fail:
while (--i >= 0)
device_remove_file(dev, balloon_attrs[i]);
device_unregister(dev);
bus_unregister(&balloon_subsys);
return error;
} }
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -88,9 +88,15 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
printk(KERN_DEBUG DRV_NAME ": %s: set bus master\n", printk(KERN_DEBUG DRV_NAME ": %s: set bus master\n",
pci_name(dev)); pci_name(dev));
pci_set_master(dev); pci_set_master(dev);
} else if (dev->is_busmaster && !is_master_cmd(value)) {
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: clear bus master\n",
pci_name(dev));
pci_clear_master(dev);
} }
if (value & PCI_COMMAND_INVALIDATE) { if (!(cmd->val & PCI_COMMAND_INVALIDATE) &&
(value & PCI_COMMAND_INVALIDATE)) {
if (unlikely(verbose_request)) if (unlikely(verbose_request))
printk(KERN_DEBUG printk(KERN_DEBUG
DRV_NAME ": %s: enable memory-write-invalidate\n", DRV_NAME ": %s: enable memory-write-invalidate\n",
@ -101,6 +107,13 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
pci_name(dev), err); pci_name(dev), err);
value &= ~PCI_COMMAND_INVALIDATE; value &= ~PCI_COMMAND_INVALIDATE;
} }
} else if ((cmd->val & PCI_COMMAND_INVALIDATE) &&
!(value & PCI_COMMAND_INVALIDATE)) {
if (unlikely(verbose_request))
printk(KERN_DEBUG
DRV_NAME ": %s: disable memory-write-invalidate\n",
pci_name(dev));
pci_clear_mwi(dev);
} }
cmd->val = value; cmd->val = value;

View File

@ -118,7 +118,7 @@ static void pcistub_device_release(struct kref *kref)
int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix, int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix,
&ppdev); &ppdev);
if (err) if (err && err != -ENOSYS)
dev_warn(&dev->dev, "MSI-X release failed (%d)\n", dev_warn(&dev->dev, "MSI-X release failed (%d)\n",
err); err);
} }
@ -402,7 +402,7 @@ static int pcistub_init_device(struct pci_dev *dev)
}; };
err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev); err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev);
if (err) if (err && err != -ENOSYS)
dev_err(&dev->dev, "MSI-X preparation failed (%d)\n", dev_err(&dev->dev, "MSI-X preparation failed (%d)\n",
err); err);
} }

View File

@ -113,7 +113,7 @@ static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref,
"Attaching to frontend resources - gnt_ref=%d evtchn=%d\n", "Attaching to frontend resources - gnt_ref=%d evtchn=%d\n",
gnt_ref, remote_evtchn); gnt_ref, remote_evtchn);
err = xenbus_map_ring_valloc(pdev->xdev, gnt_ref, &vaddr); err = xenbus_map_ring_valloc(pdev->xdev, &gnt_ref, 1, &vaddr);
if (err < 0) { if (err < 0) {
xenbus_dev_fatal(pdev->xdev, err, xenbus_dev_fatal(pdev->xdev, err,
"Error mapping other domain page in ours."); "Error mapping other domain page in ours.");

View File

@ -31,6 +31,8 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#define pr_fmt(fmt) "xen-pvscsi: " fmt
#include <stdarg.h> #include <stdarg.h>
#include <linux/module.h> #include <linux/module.h>
@ -69,9 +71,6 @@
#include <xen/interface/grant_table.h> #include <xen/interface/grant_table.h>
#include <xen/interface/io/vscsiif.h> #include <xen/interface/io/vscsiif.h>
#define DPRINTK(_f, _a...) \
pr_debug("(file=%s, line=%d) " _f, __FILE__ , __LINE__ , ## _a)
#define VSCSI_VERSION "v0.1" #define VSCSI_VERSION "v0.1"
#define VSCSI_NAMELEN 32 #define VSCSI_NAMELEN 32
@ -271,7 +270,7 @@ static void scsiback_print_status(char *sense_buffer, int errors,
{ {
struct scsiback_tpg *tpg = pending_req->v2p->tpg; struct scsiback_tpg *tpg = pending_req->v2p->tpg;
pr_err("xen-pvscsi[%s:%d] cmnd[0]=%02x -> st=%02x msg=%02x host=%02x drv=%02x\n", pr_err("[%s:%d] cmnd[0]=%02x -> st=%02x msg=%02x host=%02x drv=%02x\n",
tpg->tport->tport_name, pending_req->v2p->lun, tpg->tport->tport_name, pending_req->v2p->lun,
pending_req->cmnd[0], status_byte(errors), msg_byte(errors), pending_req->cmnd[0], status_byte(errors), msg_byte(errors),
host_byte(errors), driver_byte(errors)); host_byte(errors), driver_byte(errors));
@ -427,7 +426,7 @@ static int scsiback_gnttab_data_map_batch(struct gnttab_map_grant_ref *map,
BUG_ON(err); BUG_ON(err);
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
if (unlikely(map[i].status != GNTST_okay)) { if (unlikely(map[i].status != GNTST_okay)) {
pr_err("xen-pvscsi: invalid buffer -- could not remap it\n"); pr_err("invalid buffer -- could not remap it\n");
map[i].handle = SCSIBACK_INVALID_HANDLE; map[i].handle = SCSIBACK_INVALID_HANDLE;
err = -ENOMEM; err = -ENOMEM;
} else { } else {
@ -449,7 +448,7 @@ static int scsiback_gnttab_data_map_list(struct vscsibk_pend *pending_req,
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
if (get_free_page(pg + mapcount)) { if (get_free_page(pg + mapcount)) {
put_free_pages(pg, mapcount); put_free_pages(pg, mapcount);
pr_err("xen-pvscsi: no grant page\n"); pr_err("no grant page\n");
return -ENOMEM; return -ENOMEM;
} }
gnttab_set_map_op(&map[mapcount], vaddr_page(pg[mapcount]), gnttab_set_map_op(&map[mapcount], vaddr_page(pg[mapcount]),
@ -492,7 +491,7 @@ static int scsiback_gnttab_data_map(struct vscsiif_request *ring_req,
return 0; return 0;
if (nr_segments > VSCSIIF_SG_TABLESIZE) { if (nr_segments > VSCSIIF_SG_TABLESIZE) {
DPRINTK("xen-pvscsi: invalid parameter nr_seg = %d\n", pr_debug("invalid parameter nr_seg = %d\n",
ring_req->nr_segments); ring_req->nr_segments);
return -EINVAL; return -EINVAL;
} }
@ -516,13 +515,12 @@ static int scsiback_gnttab_data_map(struct vscsiif_request *ring_req,
nr_segments += n_segs; nr_segments += n_segs;
} }
if (nr_segments > SG_ALL) { if (nr_segments > SG_ALL) {
DPRINTK("xen-pvscsi: invalid nr_seg = %d\n", pr_debug("invalid nr_seg = %d\n", nr_segments);
nr_segments);
return -EINVAL; return -EINVAL;
} }
} }
/* free of (sgl) in fast_flush_area()*/ /* free of (sgl) in fast_flush_area() */
pending_req->sgl = kmalloc_array(nr_segments, pending_req->sgl = kmalloc_array(nr_segments,
sizeof(struct scatterlist), GFP_KERNEL); sizeof(struct scatterlist), GFP_KERNEL);
if (!pending_req->sgl) if (!pending_req->sgl)
@ -679,7 +677,8 @@ static int prepare_pending_reqs(struct vscsibk_info *info,
v2p = scsiback_do_translation(info, &vir); v2p = scsiback_do_translation(info, &vir);
if (!v2p) { if (!v2p) {
pending_req->v2p = NULL; pending_req->v2p = NULL;
DPRINTK("xen-pvscsi: doesn't exist.\n"); pr_debug("the v2p of (chn:%d, tgt:%d, lun:%d) doesn't exist.\n",
vir.chn, vir.tgt, vir.lun);
return -ENODEV; return -ENODEV;
} }
pending_req->v2p = v2p; pending_req->v2p = v2p;
@ -690,14 +689,14 @@ static int prepare_pending_reqs(struct vscsibk_info *info,
(pending_req->sc_data_direction != DMA_TO_DEVICE) && (pending_req->sc_data_direction != DMA_TO_DEVICE) &&
(pending_req->sc_data_direction != DMA_FROM_DEVICE) && (pending_req->sc_data_direction != DMA_FROM_DEVICE) &&
(pending_req->sc_data_direction != DMA_NONE)) { (pending_req->sc_data_direction != DMA_NONE)) {
DPRINTK("xen-pvscsi: invalid parameter data_dir = %d\n", pr_debug("invalid parameter data_dir = %d\n",
pending_req->sc_data_direction); pending_req->sc_data_direction);
return -EINVAL; return -EINVAL;
} }
pending_req->cmd_len = ring_req->cmd_len; pending_req->cmd_len = ring_req->cmd_len;
if (pending_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) { if (pending_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) {
DPRINTK("xen-pvscsi: invalid parameter cmd_len = %d\n", pr_debug("invalid parameter cmd_len = %d\n",
pending_req->cmd_len); pending_req->cmd_len);
return -EINVAL; return -EINVAL;
} }
@ -721,7 +720,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)
if (RING_REQUEST_PROD_OVERFLOW(ring, rp)) { if (RING_REQUEST_PROD_OVERFLOW(ring, rp)) {
rc = ring->rsp_prod_pvt; rc = ring->rsp_prod_pvt;
pr_warn("xen-pvscsi: Dom%d provided bogus ring requests (%#x - %#x = %u). Halting ring processing\n", pr_warn("Dom%d provided bogus ring requests (%#x - %#x = %u). Halting ring processing\n",
info->domid, rp, rc, rp - rc); info->domid, rp, rc, rp - rc);
info->ring_error = 1; info->ring_error = 1;
return 0; return 0;
@ -772,7 +771,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)
scsiback_device_action(pending_req, TMR_LUN_RESET, 0); scsiback_device_action(pending_req, TMR_LUN_RESET, 0);
break; break;
default: default:
pr_err_ratelimited("xen-pvscsi: invalid request\n"); pr_err_ratelimited("invalid request\n");
scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24, scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24,
0, pending_req); 0, pending_req);
kmem_cache_free(scsiback_cachep, pending_req); kmem_cache_free(scsiback_cachep, pending_req);
@ -810,7 +809,7 @@ static int scsiback_init_sring(struct vscsibk_info *info, grant_ref_t ring_ref,
if (info->irq) if (info->irq)
return -1; return -1;
err = xenbus_map_ring_valloc(info->dev, ring_ref, &area); err = xenbus_map_ring_valloc(info->dev, &ring_ref, 1, &area);
if (err) if (err)
return err; return err;
@ -874,14 +873,13 @@ static int scsiback_add_translation_entry(struct vscsibk_info *info,
lunp = strrchr(phy, ':'); lunp = strrchr(phy, ':');
if (!lunp) { if (!lunp) {
pr_err("xen-pvscsi: illegal format of physical device %s\n", pr_err("illegal format of physical device %s\n", phy);
phy);
return -EINVAL; return -EINVAL;
} }
*lunp = 0; *lunp = 0;
lunp++; lunp++;
if (kstrtouint(lunp, 10, &lun) || lun >= TRANSPORT_MAX_LUNS_PER_TPG) { if (kstrtouint(lunp, 10, &lun) || lun >= TRANSPORT_MAX_LUNS_PER_TPG) {
pr_err("xen-pvscsi: lun number not valid: %s\n", lunp); pr_err("lun number not valid: %s\n", lunp);
return -EINVAL; return -EINVAL;
} }
@ -909,7 +907,7 @@ static int scsiback_add_translation_entry(struct vscsibk_info *info,
mutex_unlock(&scsiback_mutex); mutex_unlock(&scsiback_mutex);
if (!tpg) { if (!tpg) {
pr_err("xen-pvscsi: %s:%d %s\n", phy, lun, error); pr_err("%s:%d %s\n", phy, lun, error);
return -ENODEV; return -ENODEV;
} }
@ -926,7 +924,7 @@ static int scsiback_add_translation_entry(struct vscsibk_info *info,
if ((entry->v.chn == v->chn) && if ((entry->v.chn == v->chn) &&
(entry->v.tgt == v->tgt) && (entry->v.tgt == v->tgt) &&
(entry->v.lun == v->lun)) { (entry->v.lun == v->lun)) {
pr_warn("xen-pvscsi: Virtual ID is already used. Assignment was not performed.\n"); pr_warn("Virtual ID is already used. Assignment was not performed.\n");
err = -EEXIST; err = -EEXIST;
goto out; goto out;
} }
@ -992,15 +990,15 @@ found:
} }
static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state, static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state,
char *phy, struct ids_tuple *vir) char *phy, struct ids_tuple *vir, int try)
{ {
if (!scsiback_add_translation_entry(info, phy, vir)) { if (!scsiback_add_translation_entry(info, phy, vir)) {
if (xenbus_printf(XBT_NIL, info->dev->nodename, state, if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
"%d", XenbusStateInitialised)) { "%d", XenbusStateInitialised)) {
pr_err("xen-pvscsi: xenbus_printf error %s\n", state); pr_err("xenbus_printf error %s\n", state);
scsiback_del_translation_entry(info, vir); scsiback_del_translation_entry(info, vir);
} }
} else { } else if (!try) {
xenbus_printf(XBT_NIL, info->dev->nodename, state, xenbus_printf(XBT_NIL, info->dev->nodename, state,
"%d", XenbusStateClosed); "%d", XenbusStateClosed);
} }
@ -1012,7 +1010,7 @@ static void scsiback_do_del_lun(struct vscsibk_info *info, const char *state,
if (!scsiback_del_translation_entry(info, vir)) { if (!scsiback_del_translation_entry(info, vir)) {
if (xenbus_printf(XBT_NIL, info->dev->nodename, state, if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
"%d", XenbusStateClosed)) "%d", XenbusStateClosed))
pr_err("xen-pvscsi: xenbus_printf error %s\n", state); pr_err("xenbus_printf error %s\n", state);
} }
} }
@ -1060,10 +1058,19 @@ static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op,
switch (op) { switch (op) {
case VSCSIBACK_OP_ADD_OR_DEL_LUN: case VSCSIBACK_OP_ADD_OR_DEL_LUN:
if (device_state == XenbusStateInitialising) switch (device_state) {
scsiback_do_add_lun(info, state, phy, &vir); case XenbusStateInitialising:
if (device_state == XenbusStateClosing) scsiback_do_add_lun(info, state, phy, &vir, 0);
break;
case XenbusStateConnected:
scsiback_do_add_lun(info, state, phy, &vir, 1);
break;
case XenbusStateClosing:
scsiback_do_del_lun(info, state, &vir); scsiback_do_del_lun(info, state, &vir);
break;
default:
break;
}
break; break;
case VSCSIBACK_OP_UPDATEDEV_STATE: case VSCSIBACK_OP_UPDATEDEV_STATE:
@ -1071,15 +1078,14 @@ static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op,
/* modify vscsi-devs/dev-x/state */ /* modify vscsi-devs/dev-x/state */
if (xenbus_printf(XBT_NIL, dev->nodename, state, if (xenbus_printf(XBT_NIL, dev->nodename, state,
"%d", XenbusStateConnected)) { "%d", XenbusStateConnected)) {
pr_err("xen-pvscsi: xenbus_printf error %s\n", pr_err("xenbus_printf error %s\n", str);
str);
scsiback_del_translation_entry(info, &vir); scsiback_del_translation_entry(info, &vir);
xenbus_printf(XBT_NIL, dev->nodename, state, xenbus_printf(XBT_NIL, dev->nodename, state,
"%d", XenbusStateClosed); "%d", XenbusStateClosed);
} }
} }
break; break;
/*When it is necessary, processing is added here.*/ /* When it is necessary, processing is added here. */
default: default:
break; break;
} }
@ -1196,7 +1202,7 @@ static int scsiback_probe(struct xenbus_device *dev,
struct vscsibk_info *info = kzalloc(sizeof(struct vscsibk_info), struct vscsibk_info *info = kzalloc(sizeof(struct vscsibk_info),
GFP_KERNEL); GFP_KERNEL);
DPRINTK("%p %d\n", dev, dev->otherend_id); pr_debug("%s %p %d\n", __func__, dev, dev->otherend_id);
if (!info) { if (!info) {
xenbus_dev_fatal(dev, -ENOMEM, "allocating backend structure"); xenbus_dev_fatal(dev, -ENOMEM, "allocating backend structure");
@ -1227,7 +1233,7 @@ static int scsiback_probe(struct xenbus_device *dev,
return 0; return 0;
fail: fail:
pr_warn("xen-pvscsi: %s failed\n", __func__); pr_warn("%s failed\n", __func__);
scsiback_remove(dev); scsiback_remove(dev);
return err; return err;
@ -1432,7 +1438,7 @@ check_len:
} }
snprintf(&tport->tport_name[0], VSCSI_NAMELEN, "%s", &name[off]); snprintf(&tport->tport_name[0], VSCSI_NAMELEN, "%s", &name[off]);
pr_debug("xen-pvscsi: Allocated emulated Target %s Address: %s\n", pr_debug("Allocated emulated Target %s Address: %s\n",
scsiback_dump_proto_id(tport), name); scsiback_dump_proto_id(tport), name);
return &tport->tport_wwn; return &tport->tport_wwn;
@ -1443,7 +1449,7 @@ static void scsiback_drop_tport(struct se_wwn *wwn)
struct scsiback_tport *tport = container_of(wwn, struct scsiback_tport *tport = container_of(wwn,
struct scsiback_tport, tport_wwn); struct scsiback_tport, tport_wwn);
pr_debug("xen-pvscsi: Deallocating emulated Target %s Address: %s\n", pr_debug("Deallocating emulated Target %s Address: %s\n",
scsiback_dump_proto_id(tport), tport->tport_name); scsiback_dump_proto_id(tport), tport->tport_name);
kfree(tport); kfree(tport);
@ -1470,8 +1476,8 @@ static u32 scsiback_tpg_get_inst_index(struct se_portal_group *se_tpg)
static int scsiback_check_stop_free(struct se_cmd *se_cmd) static int scsiback_check_stop_free(struct se_cmd *se_cmd)
{ {
/* /*
* Do not release struct se_cmd's containing a valid TMR * Do not release struct se_cmd's containing a valid TMR pointer.
* pointer. These will be released directly in scsiback_device_action() * These will be released directly in scsiback_device_action()
* with transport_generic_free_cmd(). * with transport_generic_free_cmd().
*/ */
if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
@ -1637,7 +1643,7 @@ static int scsiback_make_nexus(struct scsiback_tpg *tpg,
return -ENOMEM; return -ENOMEM;
} }
/* /*
* Initialize the struct se_session pointer * Initialize the struct se_session pointer
*/ */
tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL); tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
if (IS_ERR(tv_nexus->tvn_se_sess)) { if (IS_ERR(tv_nexus->tvn_se_sess)) {
@ -1705,7 +1711,7 @@ static int scsiback_drop_nexus(struct scsiback_tpg *tpg)
return -EBUSY; return -EBUSY;
} }
pr_debug("xen-pvscsi: Removing I_T Nexus to emulated %s Initiator Port: %s\n", pr_debug("Removing I_T Nexus to emulated %s Initiator Port: %s\n",
scsiback_dump_proto_id(tpg->tport), scsiback_dump_proto_id(tpg->tport),
tv_nexus->tvn_se_sess->se_node_acl->initiatorname); tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
@ -1751,7 +1757,7 @@ static ssize_t scsiback_tpg_store_nexus(struct se_portal_group *se_tpg,
unsigned char i_port[VSCSI_NAMELEN], *ptr, *port_ptr; unsigned char i_port[VSCSI_NAMELEN], *ptr, *port_ptr;
int ret; int ret;
/* /*
* Shutdown the active I_T nexus if 'NULL' is passed.. * Shutdown the active I_T nexus if 'NULL' is passed.
*/ */
if (!strncmp(page, "NULL", 4)) { if (!strncmp(page, "NULL", 4)) {
ret = scsiback_drop_nexus(tpg); ret = scsiback_drop_nexus(tpg);
@ -1922,7 +1928,7 @@ static void scsiback_drop_tpg(struct se_portal_group *se_tpg)
*/ */
scsiback_drop_nexus(tpg); scsiback_drop_nexus(tpg);
/* /*
* Deregister the se_tpg from TCM.. * Deregister the se_tpg from TCM.
*/ */
core_tpg_deregister(se_tpg); core_tpg_deregister(se_tpg);
kfree(tpg); kfree(tpg);
@ -1992,7 +1998,7 @@ static int scsiback_register_configfs(void)
struct target_fabric_configfs *fabric; struct target_fabric_configfs *fabric;
int ret; int ret;
pr_debug("xen-pvscsi: fabric module %s on %s/%s on "UTS_RELEASE"\n", pr_debug("fabric module %s on %s/%s on "UTS_RELEASE"\n",
VSCSI_VERSION, utsname()->sysname, utsname()->machine); VSCSI_VERSION, utsname()->sysname, utsname()->machine);
/* /*
* Register the top level struct config_item_type with TCM core * Register the top level struct config_item_type with TCM core
@ -2029,7 +2035,7 @@ static int scsiback_register_configfs(void)
* Setup our local pointer to *fabric * Setup our local pointer to *fabric
*/ */
scsiback_fabric_configfs = fabric; scsiback_fabric_configfs = fabric;
pr_debug("xen-pvscsi: Set fabric -> scsiback_fabric_configfs\n"); pr_debug("Set fabric -> scsiback_fabric_configfs\n");
return 0; return 0;
}; };
@ -2040,7 +2046,7 @@ static void scsiback_deregister_configfs(void)
target_fabric_configfs_deregister(scsiback_fabric_configfs); target_fabric_configfs_deregister(scsiback_fabric_configfs);
scsiback_fabric_configfs = NULL; scsiback_fabric_configfs = NULL;
pr_debug("xen-pvscsi: Cleared scsiback_fabric_configfs\n"); pr_debug("Cleared scsiback_fabric_configfs\n");
}; };
static const struct xenbus_device_id scsiback_ids[] = { static const struct xenbus_device_id scsiback_ids[] = {
@ -2091,7 +2097,7 @@ out_unregister_xenbus:
xenbus_unregister_driver(&scsiback_driver); xenbus_unregister_driver(&scsiback_driver);
out_cache_destroy: out_cache_destroy:
kmem_cache_destroy(scsiback_cachep); kmem_cache_destroy(scsiback_cachep);
pr_err("xen-pvscsi: %s: error %d\n", __func__, ret); pr_err("%s: error %d\n", __func__, ret);
return ret; return ret;
} }

View File

@ -52,17 +52,25 @@
struct xenbus_map_node { struct xenbus_map_node {
struct list_head next; struct list_head next;
union { union {
struct vm_struct *area; /* PV */ struct {
struct page *page; /* HVM */ struct vm_struct *area;
} pv;
struct {
struct page *pages[XENBUS_MAX_RING_PAGES];
void *addr;
} hvm;
}; };
grant_handle_t handle; grant_handle_t handles[XENBUS_MAX_RING_PAGES];
unsigned int nr_handles;
}; };
static DEFINE_SPINLOCK(xenbus_valloc_lock); static DEFINE_SPINLOCK(xenbus_valloc_lock);
static LIST_HEAD(xenbus_valloc_pages); static LIST_HEAD(xenbus_valloc_pages);
struct xenbus_ring_ops { struct xenbus_ring_ops {
int (*map)(struct xenbus_device *dev, int gnt, void **vaddr); int (*map)(struct xenbus_device *dev,
grant_ref_t *gnt_refs, unsigned int nr_grefs,
void **vaddr);
int (*unmap)(struct xenbus_device *dev, void *vaddr); int (*unmap)(struct xenbus_device *dev, void *vaddr);
}; };
@ -355,17 +363,39 @@ static void xenbus_switch_fatal(struct xenbus_device *dev, int depth, int err,
/** /**
* xenbus_grant_ring * xenbus_grant_ring
* @dev: xenbus device * @dev: xenbus device
* @ring_mfn: mfn of ring to grant * @vaddr: starting virtual address of the ring
* @nr_pages: number of pages to be granted
* Grant access to the given @ring_mfn to the peer of the given device. Return * @grefs: grant reference array to be filled in
* a grant reference on success, or -errno on error. On error, the device will *
* switch to XenbusStateClosing, and the error will be saved in the store. * Grant access to the given @vaddr to the peer of the given device.
* Then fill in @grefs with grant references. Return 0 on success, or
* -errno on error. On error, the device will switch to
* XenbusStateClosing, and the error will be saved in the store.
*/ */
int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn) int xenbus_grant_ring(struct xenbus_device *dev, void *vaddr,
unsigned int nr_pages, grant_ref_t *grefs)
{ {
int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0); int err;
if (err < 0) int i, j;
xenbus_dev_fatal(dev, err, "granting access to ring page");
for (i = 0; i < nr_pages; i++) {
unsigned long addr = (unsigned long)vaddr +
(PAGE_SIZE * i);
err = gnttab_grant_foreign_access(dev->otherend_id,
virt_to_mfn(addr), 0);
if (err < 0) {
xenbus_dev_fatal(dev, err,
"granting access to ring page");
goto fail;
}
grefs[i] = err;
}
return 0;
fail:
for (j = 0; j < i; j++)
gnttab_end_foreign_access_ref(grefs[j], 0);
return err; return err;
} }
EXPORT_SYMBOL_GPL(xenbus_grant_ring); EXPORT_SYMBOL_GPL(xenbus_grant_ring);
@ -419,62 +449,130 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn);
/** /**
* xenbus_map_ring_valloc * xenbus_map_ring_valloc
* @dev: xenbus device * @dev: xenbus device
* @gnt_ref: grant reference * @gnt_refs: grant reference array
* @nr_grefs: number of grant references
* @vaddr: pointer to address to be filled out by mapping * @vaddr: pointer to address to be filled out by mapping
* *
* Based on Rusty Russell's skeleton driver's map_page. * Map @nr_grefs pages of memory into this domain from another
* Map a page of memory into this domain from another domain's grant table. * domain's grant table. xenbus_map_ring_valloc allocates @nr_grefs
* xenbus_map_ring_valloc allocates a page of virtual address space, maps the * pages of virtual address space, maps the pages to that address, and
* page to that address, and sets *vaddr to that address. * sets *vaddr to that address. Returns 0 on success, and GNTST_*
* Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h) * (see xen/include/interface/grant_table.h) or -ENOMEM / -EINVAL on
* or -ENOMEM on error. If an error is returned, device will switch to * error. If an error is returned, device will switch to
* XenbusStateClosing and the error message will be saved in XenStore. * XenbusStateClosing and the error message will be saved in XenStore.
*/ */
int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) int xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t *gnt_refs,
unsigned int nr_grefs, void **vaddr)
{ {
return ring_ops->map(dev, gnt_ref, vaddr); return ring_ops->map(dev, gnt_refs, nr_grefs, vaddr);
} }
EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc); EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc);
static int xenbus_map_ring_valloc_pv(struct xenbus_device *dev, /* N.B. sizeof(phys_addr_t) doesn't always equal to sizeof(unsigned
int gnt_ref, void **vaddr) * long), e.g. 32-on-64. Caller is responsible for preparing the
* right array to feed into this function */
static int __xenbus_map_ring(struct xenbus_device *dev,
grant_ref_t *gnt_refs,
unsigned int nr_grefs,
grant_handle_t *handles,
phys_addr_t *addrs,
unsigned int flags,
bool *leaked)
{
struct gnttab_map_grant_ref map[XENBUS_MAX_RING_PAGES];
struct gnttab_unmap_grant_ref unmap[XENBUS_MAX_RING_PAGES];
int i, j;
int err = GNTST_okay;
if (nr_grefs > XENBUS_MAX_RING_PAGES)
return -EINVAL;
for (i = 0; i < nr_grefs; i++) {
memset(&map[i], 0, sizeof(map[i]));
gnttab_set_map_op(&map[i], addrs[i], flags, gnt_refs[i],
dev->otherend_id);
handles[i] = INVALID_GRANT_HANDLE;
}
gnttab_batch_map(map, i);
for (i = 0; i < nr_grefs; i++) {
if (map[i].status != GNTST_okay) {
err = map[i].status;
xenbus_dev_fatal(dev, map[i].status,
"mapping in shared page %d from domain %d",
gnt_refs[i], dev->otherend_id);
goto fail;
} else
handles[i] = map[i].handle;
}
return GNTST_okay;
fail:
for (i = j = 0; i < nr_grefs; i++) {
if (handles[i] != INVALID_GRANT_HANDLE) {
memset(&unmap[j], 0, sizeof(unmap[j]));
gnttab_set_unmap_op(&unmap[j], (phys_addr_t)addrs[i],
GNTMAP_host_map, handles[i]);
j++;
}
}
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap, j))
BUG();
*leaked = false;
for (i = 0; i < j; i++) {
if (unmap[i].status != GNTST_okay) {
*leaked = true;
break;
}
}
return err;
}
static int xenbus_map_ring_valloc_pv(struct xenbus_device *dev,
grant_ref_t *gnt_refs,
unsigned int nr_grefs,
void **vaddr)
{ {
struct gnttab_map_grant_ref op = {
.flags = GNTMAP_host_map | GNTMAP_contains_pte,
.ref = gnt_ref,
.dom = dev->otherend_id,
};
struct xenbus_map_node *node; struct xenbus_map_node *node;
struct vm_struct *area; struct vm_struct *area;
pte_t *pte; pte_t *ptes[XENBUS_MAX_RING_PAGES];
phys_addr_t phys_addrs[XENBUS_MAX_RING_PAGES];
int err = GNTST_okay;
int i;
bool leaked;
*vaddr = NULL; *vaddr = NULL;
if (nr_grefs > XENBUS_MAX_RING_PAGES)
return -EINVAL;
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) if (!node)
return -ENOMEM; return -ENOMEM;
area = alloc_vm_area(PAGE_SIZE, &pte); area = alloc_vm_area(PAGE_SIZE * nr_grefs, ptes);
if (!area) { if (!area) {
kfree(node); kfree(node);
return -ENOMEM; return -ENOMEM;
} }
op.host_addr = arbitrary_virt_to_machine(pte).maddr; for (i = 0; i < nr_grefs; i++)
phys_addrs[i] = arbitrary_virt_to_machine(ptes[i]).maddr;
gnttab_batch_map(&op, 1); err = __xenbus_map_ring(dev, gnt_refs, nr_grefs, node->handles,
phys_addrs,
GNTMAP_host_map | GNTMAP_contains_pte,
&leaked);
if (err)
goto failed;
if (op.status != GNTST_okay) { node->nr_handles = nr_grefs;
free_vm_area(area); node->pv.area = area;
kfree(node);
xenbus_dev_fatal(dev, op.status,
"mapping in shared page %d from domain %d",
gnt_ref, dev->otherend_id);
return op.status;
}
node->handle = op.handle;
node->area = area;
spin_lock(&xenbus_valloc_lock); spin_lock(&xenbus_valloc_lock);
list_add(&node->next, &xenbus_valloc_pages); list_add(&node->next, &xenbus_valloc_pages);
@ -482,14 +580,33 @@ static int xenbus_map_ring_valloc_pv(struct xenbus_device *dev,
*vaddr = area->addr; *vaddr = area->addr;
return 0; return 0;
failed:
if (!leaked)
free_vm_area(area);
else
pr_alert("leaking VM area %p size %u page(s)", area, nr_grefs);
kfree(node);
return err;
} }
static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev, static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
int gnt_ref, void **vaddr) grant_ref_t *gnt_ref,
unsigned int nr_grefs,
void **vaddr)
{ {
struct xenbus_map_node *node; struct xenbus_map_node *node;
int i;
int err; int err;
void *addr; void *addr;
bool leaked = false;
/* Why do we need two arrays? See comment of __xenbus_map_ring */
phys_addr_t phys_addrs[XENBUS_MAX_RING_PAGES];
unsigned long addrs[XENBUS_MAX_RING_PAGES];
if (nr_grefs > XENBUS_MAX_RING_PAGES)
return -EINVAL;
*vaddr = NULL; *vaddr = NULL;
@ -497,15 +614,32 @@ static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
if (!node) if (!node)
return -ENOMEM; return -ENOMEM;
err = alloc_xenballooned_pages(1, &node->page, false /* lowmem */); err = alloc_xenballooned_pages(nr_grefs, node->hvm.pages,
false /* lowmem */);
if (err) if (err)
goto out_err; goto out_err;
addr = pfn_to_kaddr(page_to_pfn(node->page)); for (i = 0; i < nr_grefs; i++) {
unsigned long pfn = page_to_pfn(node->hvm.pages[i]);
phys_addrs[i] = (unsigned long)pfn_to_kaddr(pfn);
addrs[i] = (unsigned long)pfn_to_kaddr(pfn);
}
err = __xenbus_map_ring(dev, gnt_ref, nr_grefs, node->handles,
phys_addrs, GNTMAP_host_map, &leaked);
node->nr_handles = nr_grefs;
err = xenbus_map_ring(dev, gnt_ref, &node->handle, addr);
if (err) if (err)
goto out_err_free_ballooned_pages; goto out_free_ballooned_pages;
addr = vmap(node->hvm.pages, nr_grefs, VM_MAP | VM_IOREMAP,
PAGE_KERNEL);
if (!addr) {
err = -ENOMEM;
goto out_xenbus_unmap_ring;
}
node->hvm.addr = addr;
spin_lock(&xenbus_valloc_lock); spin_lock(&xenbus_valloc_lock);
list_add(&node->next, &xenbus_valloc_pages); list_add(&node->next, &xenbus_valloc_pages);
@ -514,8 +648,16 @@ static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
*vaddr = addr; *vaddr = addr;
return 0; return 0;
out_err_free_ballooned_pages: out_xenbus_unmap_ring:
free_xenballooned_pages(1, &node->page); if (!leaked)
xenbus_unmap_ring(dev, node->handles, node->nr_handles,
addrs);
else
pr_alert("leaking %p size %u page(s)",
addr, nr_grefs);
out_free_ballooned_pages:
if (!leaked)
free_xenballooned_pages(nr_grefs, node->hvm.pages);
out_err: out_err:
kfree(node); kfree(node);
return err; return err;
@ -525,35 +667,37 @@ static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
/** /**
* xenbus_map_ring * xenbus_map_ring
* @dev: xenbus device * @dev: xenbus device
* @gnt_ref: grant reference * @gnt_refs: grant reference array
* @handle: pointer to grant handle to be filled * @nr_grefs: number of grant reference
* @vaddr: address to be mapped to * @handles: pointer to grant handle to be filled
* @vaddrs: addresses to be mapped to
* @leaked: fail to clean up a failed map, caller should not free vaddr
* *
* Map a page of memory into this domain from another domain's grant table. * Map pages of memory into this domain from another domain's grant table.
* xenbus_map_ring does not allocate the virtual address space (you must do * xenbus_map_ring does not allocate the virtual address space (you must do
* this yourself!). It only maps in the page to the specified address. * this yourself!). It only maps in the pages to the specified address.
* Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h) * Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h)
* or -ENOMEM on error. If an error is returned, device will switch to * or -ENOMEM / -EINVAL on error. If an error is returned, device will switch to
* XenbusStateClosing and the error message will be saved in XenStore. * XenbusStateClosing and the first error message will be saved in XenStore.
* Further more if we fail to map the ring, caller should check @leaked.
* If @leaked is not zero it means xenbus_map_ring fails to clean up, caller
* should not free the address space of @vaddr.
*/ */
int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref, int xenbus_map_ring(struct xenbus_device *dev, grant_ref_t *gnt_refs,
grant_handle_t *handle, void *vaddr) unsigned int nr_grefs, grant_handle_t *handles,
unsigned long *vaddrs, bool *leaked)
{ {
struct gnttab_map_grant_ref op; phys_addr_t phys_addrs[XENBUS_MAX_RING_PAGES];
int i;
gnttab_set_map_op(&op, (unsigned long)vaddr, GNTMAP_host_map, gnt_ref, if (nr_grefs > XENBUS_MAX_RING_PAGES)
dev->otherend_id); return -EINVAL;
gnttab_batch_map(&op, 1); for (i = 0; i < nr_grefs; i++)
phys_addrs[i] = (unsigned long)vaddrs[i];
if (op.status != GNTST_okay) { return __xenbus_map_ring(dev, gnt_refs, nr_grefs, handles,
xenbus_dev_fatal(dev, op.status, phys_addrs, GNTMAP_host_map, leaked);
"mapping in shared page %d from domain %d",
gnt_ref, dev->otherend_id);
} else
*handle = op.handle;
return op.status;
} }
EXPORT_SYMBOL_GPL(xenbus_map_ring); EXPORT_SYMBOL_GPL(xenbus_map_ring);
@ -579,14 +723,15 @@ EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree);
static int xenbus_unmap_ring_vfree_pv(struct xenbus_device *dev, void *vaddr) static int xenbus_unmap_ring_vfree_pv(struct xenbus_device *dev, void *vaddr)
{ {
struct xenbus_map_node *node; struct xenbus_map_node *node;
struct gnttab_unmap_grant_ref op = { struct gnttab_unmap_grant_ref unmap[XENBUS_MAX_RING_PAGES];
.host_addr = (unsigned long)vaddr,
};
unsigned int level; unsigned int level;
int i;
bool leaked = false;
int err;
spin_lock(&xenbus_valloc_lock); spin_lock(&xenbus_valloc_lock);
list_for_each_entry(node, &xenbus_valloc_pages, next) { list_for_each_entry(node, &xenbus_valloc_pages, next) {
if (node->area->addr == vaddr) { if (node->pv.area->addr == vaddr) {
list_del(&node->next); list_del(&node->next);
goto found; goto found;
} }
@ -601,22 +746,41 @@ static int xenbus_unmap_ring_vfree_pv(struct xenbus_device *dev, void *vaddr)
return GNTST_bad_virt_addr; return GNTST_bad_virt_addr;
} }
op.handle = node->handle; for (i = 0; i < node->nr_handles; i++) {
op.host_addr = arbitrary_virt_to_machine( unsigned long addr;
lookup_address((unsigned long)vaddr, &level)).maddr;
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1)) memset(&unmap[i], 0, sizeof(unmap[i]));
addr = (unsigned long)vaddr + (PAGE_SIZE * i);
unmap[i].host_addr = arbitrary_virt_to_machine(
lookup_address(addr, &level)).maddr;
unmap[i].dev_bus_addr = 0;
unmap[i].handle = node->handles[i];
}
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap, i))
BUG(); BUG();
if (op.status == GNTST_okay) err = GNTST_okay;
free_vm_area(node->area); leaked = false;
for (i = 0; i < node->nr_handles; i++) {
if (unmap[i].status != GNTST_okay) {
leaked = true;
xenbus_dev_error(dev, unmap[i].status,
"unmapping page at handle %d error %d",
node->handles[i], unmap[i].status);
err = unmap[i].status;
break;
}
}
if (!leaked)
free_vm_area(node->pv.area);
else else
xenbus_dev_error(dev, op.status, pr_alert("leaking VM area %p size %u page(s)",
"unmapping page at handle %d error %d", node->pv.area, node->nr_handles);
node->handle, op.status);
kfree(node); kfree(node);
return op.status; return err;
} }
static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr) static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
@ -624,10 +788,12 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
int rv; int rv;
struct xenbus_map_node *node; struct xenbus_map_node *node;
void *addr; void *addr;
unsigned long addrs[XENBUS_MAX_RING_PAGES];
int i;
spin_lock(&xenbus_valloc_lock); spin_lock(&xenbus_valloc_lock);
list_for_each_entry(node, &xenbus_valloc_pages, next) { list_for_each_entry(node, &xenbus_valloc_pages, next) {
addr = pfn_to_kaddr(page_to_pfn(node->page)); addr = node->hvm.addr;
if (addr == vaddr) { if (addr == vaddr) {
list_del(&node->next); list_del(&node->next);
goto found; goto found;
@ -643,12 +809,16 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
return GNTST_bad_virt_addr; return GNTST_bad_virt_addr;
} }
rv = xenbus_unmap_ring(dev, node->handle, addr); for (i = 0; i < node->nr_handles; i++)
addrs[i] = (unsigned long)pfn_to_kaddr(page_to_pfn(node->hvm.pages[i]));
rv = xenbus_unmap_ring(dev, node->handles, node->nr_handles,
addrs);
if (!rv) if (!rv)
free_xenballooned_pages(1, &node->page); vunmap(vaddr);
else else
WARN(1, "Leaking %p\n", vaddr); WARN(1, "Leaking %p, size %u page(s)\n", vaddr,
node->nr_handles);
kfree(node); kfree(node);
return rv; return rv;
@ -657,29 +827,44 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
/** /**
* xenbus_unmap_ring * xenbus_unmap_ring
* @dev: xenbus device * @dev: xenbus device
* @handle: grant handle * @handles: grant handle array
* @vaddr: addr to unmap * @nr_handles: number of handles in the array
* @vaddrs: addresses to unmap
* *
* Unmap a page of memory in this domain that was imported from another domain. * Unmap memory in this domain that was imported from another domain.
* Returns 0 on success and returns GNTST_* on error * Returns 0 on success and returns GNTST_* on error
* (see xen/include/interface/grant_table.h). * (see xen/include/interface/grant_table.h).
*/ */
int xenbus_unmap_ring(struct xenbus_device *dev, int xenbus_unmap_ring(struct xenbus_device *dev,
grant_handle_t handle, void *vaddr) grant_handle_t *handles, unsigned int nr_handles,
unsigned long *vaddrs)
{ {
struct gnttab_unmap_grant_ref op; struct gnttab_unmap_grant_ref unmap[XENBUS_MAX_RING_PAGES];
int i;
int err;
gnttab_set_unmap_op(&op, (unsigned long)vaddr, GNTMAP_host_map, handle); if (nr_handles > XENBUS_MAX_RING_PAGES)
return -EINVAL;
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1)) for (i = 0; i < nr_handles; i++)
gnttab_set_unmap_op(&unmap[i], vaddrs[i],
GNTMAP_host_map, handles[i]);
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap, i))
BUG(); BUG();
if (op.status != GNTST_okay) err = GNTST_okay;
xenbus_dev_error(dev, op.status, for (i = 0; i < nr_handles; i++) {
"unmapping page at handle %d error %d", if (unmap[i].status != GNTST_okay) {
handle, op.status); xenbus_dev_error(dev, unmap[i].status,
"unmapping page at handle %d error %d",
handles[i], unmap[i].status);
err = unmap[i].status;
break;
}
}
return op.status; return err;
} }
EXPORT_SYMBOL_GPL(xenbus_unmap_ring); EXPORT_SYMBOL_GPL(xenbus_unmap_ring);

143
drivers/xen/xlate_mmu.c Normal file
View File

@ -0,0 +1,143 @@
/*
* MMU operations common to all auto-translated physmap guests.
*
* Copyright (C) 2015 Citrix Systems R&D Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation; or, when distributed
* separately from the Linux kernel or incorporated into other
* software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/hypervisor.h>
#include <xen/xen.h>
#include <xen/page.h>
#include <xen/interface/xen.h>
#include <xen/interface/memory.h>
/* map fgmfn of domid to lpfn in the current domain */
static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn,
unsigned int domid)
{
int rc;
struct xen_add_to_physmap_range xatp = {
.domid = DOMID_SELF,
.foreign_domid = domid,
.size = 1,
.space = XENMAPSPACE_gmfn_foreign,
};
xen_ulong_t idx = fgmfn;
xen_pfn_t gpfn = lpfn;
int err = 0;
set_xen_guest_handle(xatp.idxs, &idx);
set_xen_guest_handle(xatp.gpfns, &gpfn);
set_xen_guest_handle(xatp.errs, &err);
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp);
return rc < 0 ? rc : err;
}
struct remap_data {
xen_pfn_t *fgmfn; /* foreign domain's gmfn */
pgprot_t prot;
domid_t domid;
struct vm_area_struct *vma;
int index;
struct page **pages;
struct xen_remap_mfn_info *info;
int *err_ptr;
int mapped;
};
static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr,
void *data)
{
struct remap_data *info = data;
struct page *page = info->pages[info->index++];
unsigned long pfn = page_to_pfn(page);
pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot));
int rc;
rc = map_foreign_page(pfn, *info->fgmfn, info->domid);
*info->err_ptr++ = rc;
if (!rc) {
set_pte_at(info->vma->vm_mm, addr, ptep, pte);
info->mapped++;
}
info->fgmfn++;
return 0;
}
int xen_xlate_remap_gfn_array(struct vm_area_struct *vma,
unsigned long addr,
xen_pfn_t *mfn, int nr,
int *err_ptr, pgprot_t prot,
unsigned domid,
struct page **pages)
{
int err;
struct remap_data data;
unsigned long range = nr << PAGE_SHIFT;
/* Kept here for the purpose of making sure code doesn't break
x86 PVOPS */
BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO)));
data.fgmfn = mfn;
data.prot = prot;
data.domid = domid;
data.vma = vma;
data.pages = pages;
data.index = 0;
data.err_ptr = err_ptr;
data.mapped = 0;
err = apply_to_page_range(vma->vm_mm, addr, range,
remap_pte_fn, &data);
return err < 0 ? err : data.mapped;
}
EXPORT_SYMBOL_GPL(xen_xlate_remap_gfn_array);
int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma,
int nr, struct page **pages)
{
int i;
for (i = 0; i < nr; i++) {
struct xen_remove_from_physmap xrp;
unsigned long pfn;
pfn = page_to_pfn(pages[i]);
xrp.domid = DOMID_SELF;
xrp.gpfn = pfn;
(void)HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp);
}
return 0;
}
EXPORT_SYMBOL_GPL(xen_xlate_unmap_gfn_range);

View File

@ -67,7 +67,7 @@
#define __HYPERVISOR_vcpu_op 24 #define __HYPERVISOR_vcpu_op 24
#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ #define __HYPERVISOR_set_segment_base 25 /* x86/64 only */
#define __HYPERVISOR_mmuext_op 26 #define __HYPERVISOR_mmuext_op 26
#define __HYPERVISOR_acm_op 27 #define __HYPERVISOR_xsm_op 27
#define __HYPERVISOR_nmi_op 28 #define __HYPERVISOR_nmi_op 28
#define __HYPERVISOR_sched_op 29 #define __HYPERVISOR_sched_op 29
#define __HYPERVISOR_callback_op 30 #define __HYPERVISOR_callback_op 30
@ -75,7 +75,11 @@
#define __HYPERVISOR_event_channel_op 32 #define __HYPERVISOR_event_channel_op 32
#define __HYPERVISOR_physdev_op 33 #define __HYPERVISOR_physdev_op 33
#define __HYPERVISOR_hvm_op 34 #define __HYPERVISOR_hvm_op 34
#define __HYPERVISOR_sysctl 35
#define __HYPERVISOR_domctl 36
#define __HYPERVISOR_kexec_op 37
#define __HYPERVISOR_tmem_op 38 #define __HYPERVISOR_tmem_op 38
#define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */
/* Architecture-specific hypercall definitions. */ /* Architecture-specific hypercall definitions. */
#define __HYPERVISOR_arch_0 48 #define __HYPERVISOR_arch_0 48

View File

@ -27,13 +27,58 @@ int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order); void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order);
struct vm_area_struct; struct vm_area_struct;
/*
* xen_remap_domain_mfn_array() - map an array of foreign frames
* @vma: VMA to map the pages into
* @addr: Address at which to map the pages
* @gfn: Array of GFNs to map
* @nr: Number entries in the GFN array
* @err_ptr: Returns per-GFN error status.
* @prot: page protection mask
* @domid: Domain owning the pages
* @pages: Array of pages if this domain has an auto-translated physmap
*
* @gfn and @err_ptr may point to the same buffer, the GFNs will be
* overwritten by the error codes after they are mapped.
*
* Returns the number of successfully mapped frames, or a -ve error
* code.
*/
int xen_remap_domain_mfn_array(struct vm_area_struct *vma,
unsigned long addr,
xen_pfn_t *gfn, int nr,
int *err_ptr, pgprot_t prot,
unsigned domid,
struct page **pages);
/* xen_remap_domain_mfn_range() - map a range of foreign frames
* @vma: VMA to map the pages into
* @addr: Address at which to map the pages
* @gfn: First GFN to map.
* @nr: Number frames to map
* @prot: page protection mask
* @domid: Domain owning the pages
* @pages: Array of pages if this domain has an auto-translated physmap
*
* Returns the number of successfully mapped frames, or a -ve error
* code.
*/
int xen_remap_domain_mfn_range(struct vm_area_struct *vma, int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
unsigned long addr, unsigned long addr,
xen_pfn_t mfn, int nr, xen_pfn_t gfn, int nr,
pgprot_t prot, unsigned domid, pgprot_t prot, unsigned domid,
struct page **pages); struct page **pages);
int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
int numpgs, struct page **pages); int numpgs, struct page **pages);
int xen_xlate_remap_gfn_array(struct vm_area_struct *vma,
unsigned long addr,
xen_pfn_t *gfn, int nr,
int *err_ptr, pgprot_t prot,
unsigned domid,
struct page **pages);
int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma,
int nr, struct page **pages);
bool xen_running_on_version_or_later(unsigned int major, unsigned int minor); bool xen_running_on_version_or_later(unsigned int major, unsigned int minor);

View File

@ -46,6 +46,10 @@
#include <xen/interface/io/xenbus.h> #include <xen/interface/io/xenbus.h>
#include <xen/interface/io/xs_wire.h> #include <xen/interface/io/xs_wire.h>
#define XENBUS_MAX_RING_PAGE_ORDER 4
#define XENBUS_MAX_RING_PAGES (1U << XENBUS_MAX_RING_PAGE_ORDER)
#define INVALID_GRANT_HANDLE (~0U)
/* Register callback to watch this node. */ /* Register callback to watch this node. */
struct xenbus_watch struct xenbus_watch
{ {
@ -199,15 +203,19 @@ int xenbus_watch_pathfmt(struct xenbus_device *dev, struct xenbus_watch *watch,
const char *pathfmt, ...); const char *pathfmt, ...);
int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state new_state); int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state new_state);
int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn); int xenbus_grant_ring(struct xenbus_device *dev, void *vaddr,
int xenbus_map_ring_valloc(struct xenbus_device *dev, unsigned int nr_pages, grant_ref_t *grefs);
int gnt_ref, void **vaddr); int xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t *gnt_refs,
int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref, unsigned int nr_grefs, void **vaddr);
grant_handle_t *handle, void *vaddr); int xenbus_map_ring(struct xenbus_device *dev,
grant_ref_t *gnt_refs, unsigned int nr_grefs,
grant_handle_t *handles, unsigned long *vaddrs,
bool *leaked);
int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr); int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr);
int xenbus_unmap_ring(struct xenbus_device *dev, int xenbus_unmap_ring(struct xenbus_device *dev,
grant_handle_t handle, void *vaddr); grant_handle_t *handles, unsigned int nr_handles,
unsigned long *vaddrs);
int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port); int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
int xenbus_free_evtchn(struct xenbus_device *dev, int port); int xenbus_free_evtchn(struct xenbus_device *dev, int port);

12
scripts/xen-hypercalls.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/sh
out="$1"
shift
in="$@"
for i in $in; do
eval $CPP $LINUXINCLUDE -dD -imacros "$i" -x c /dev/null
done | \
awk '$1 == "#define" && $2 ~ /__HYPERVISOR_[a-z][a-z_0-9]*/ { v[$3] = $2 }
END { print "/* auto-generated by scripts/xen-hypercall.sh */"
for (i in v) if (!(v[i] in v))
print "HYPERCALL("substr(v[i], 14)")"}' | sort -u >$out