diff --git a/src/common_map.c b/src/common_map.c index 8757151..13f0afc 100644 --- a/src/common_map.c +++ b/src/common_map.c @@ -51,5 +51,11 @@ _pci_hidden int pci_device_generic_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { +#ifdef __alpha__ + if (map->pre_bwx && map->memory_sparse) { + munmap(map->memory_sparse, map->size*(1<<5)); + } +#endif + return (munmap(map->memory, map->size) == -1) ? errno : 0; } diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c index 1832ee7..360dfe8 100644 --- a/src/linux_sysfs.c +++ b/src/linux_sysfs.c @@ -548,10 +548,29 @@ pci_device_linux_sysfs_map_range(struct pci_device *dev, map->region); fd = open(name, open_flags); + +#ifdef __alpha__ + map->pre_bwx = 0; + map->memory_sparse = NULL; if (fd == -1) { - return errno; + /* Attempt to map resourceX_dense which exists on non-BWX Alphas. */ + snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_dense", + SYS_BUS_PCI, + dev->domain, + dev->bus, + dev->dev, + dev->func, + map->region); + fd = open(name, open_flags); + if (fd != -1) { + map->pre_bwx = 1; + } } +#endif + if (fd == -1) { + return errno; + } map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); if (map->memory == MAP_FAILED) { @@ -597,7 +616,60 @@ pci_device_linux_sysfs_map_range(struct pci_device *dev, } #endif - close(fd); +#ifdef __alpha__ + /* The logic here for determining whether a sparse map is needed is not + * ideal, but it's probably as good as we can do without extra + * information from the caller telling us the intended use of the map. + * If we don't allocate the map when it is needed then segmentation + * violations are possible downstream; if we allocate the map when it is + * not needed then we waste valuable page table entries. The intent is + * to err slightly on the side of allocating the sparse map. + */ + + if (map->pre_bwx && (dev->regions[map->region].is_IO || map->size <= 131072)) { + /* We map sparse for IO and small memory mappings. This map is only + * needed when byte or word read/writes are made to IO ports. + */ + close(fd); + + snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_sparse", + SYS_BUS_PCI, + dev->domain, + dev->bus, + dev->dev, + dev->func, + map->region); + fd = open(name, open_flags); + if (fd == -1 && dev->regions[map->region].is_IO) { + /* If this is an IO map we fail -- any attempt at a byte or word + * access to the dense mapping is likely to result in + * unpredictable behaviour. + */ + int saved_errno = errno; + map->memory_sparse = NULL; + pci_device_generic_unmap_range(dev, map); + return saved_errno; + } + + /* But if a memory map doesn't have a sparse resource we continue + * without the sparse map -- maybe it's not MMIO anyway. + */ + if (fd != -1) { + map->memory_sparse = mmap(NULL, map->size*(1<<5), prot, + MAP_SHARED, fd, offset*(1<<5)); + if (map->memory_sparse == MAP_FAILED) { + int saved_errno = errno; + map->memory_sparse = NULL; + pci_device_generic_unmap_range(dev, map); + close(fd); + return saved_errno; + } + } + } +#endif + + if (fd != -1) + close(fd); return 0; } diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index 77eb57b..e0828f6 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -85,6 +85,10 @@ struct pci_device_mapping { unsigned region; unsigned flags; void *memory; +#ifdef __alpha__ + void *memory_sparse; + int pre_bwx; +#endif }; struct pci_io_handle {