Loading Documentation/filesystems/xip.txt +9 −6 Original line number Diff line number Diff line Loading @@ -28,12 +28,15 @@ Implementation Execute-in-place is implemented in three steps: block device operation, address space operation, and file operations. A block device operation named direct_access is used to retrieve a reference (pointer) to a block on-disk. The reference is supposed to be cpu-addressable, physical address and remain valid until the release operation is performed. A struct block_device reference is used to address the device, and a sector_t argument is used to identify the individual block. As an alternative, memory technology devices can be used for this. A block device operation named direct_access is used to translate the block device sector number to a page frame number (pfn) that identifies the physical page for the memory. It also returns a kernel virtual address that can be used to access the memory. The direct_access method takes a 'size' parameter that indicates the number of bytes being requested. The function should return the number of bytes that can be contiguously accessed at that offset. It may also return a negative errno if an error occurs. The block device operation is optional, these block devices support it as of today: Loading arch/powerpc/sysdev/axonram.c +4 −13 Original line number Diff line number Diff line Loading @@ -139,26 +139,17 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) * axon_ram_direct_access - direct_access() method for block device * @device, @sector, @data: see block_device_operations method */ static int static long axon_ram_direct_access(struct block_device *device, sector_t sector, void **kaddr, unsigned long *pfn) void **kaddr, unsigned long *pfn, long size) { struct axon_ram_bank *bank = device->bd_disk->private_data; loff_t offset; offset = sector; if (device->bd_part != NULL) offset += device->bd_part->start_sect; offset <<= AXON_RAM_SECTOR_SHIFT; if (offset >= bank->size) { dev_err(&bank->device->dev, "Access outside of address space\n"); return -ERANGE; } loff_t offset = (loff_t)sector << AXON_RAM_SECTOR_SHIFT; *kaddr = (void *)(bank->ph_addr + offset); *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; return 0; return bank->size - offset; } static const struct block_device_operations axon_ram_devops = { Loading drivers/block/brd.c +7 −7 Original line number Diff line number Diff line Loading @@ -370,25 +370,25 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector, } #ifdef CONFIG_BLK_DEV_XIP static int brd_direct_access(struct block_device *bdev, sector_t sector, void **kaddr, unsigned long *pfn) static long brd_direct_access(struct block_device *bdev, sector_t sector, void **kaddr, unsigned long *pfn, long size) { struct brd_device *brd = bdev->bd_disk->private_data; struct page *page; if (!brd) return -ENODEV; if (sector & (PAGE_SECTORS-1)) return -EINVAL; if (sector + PAGE_SECTORS > get_capacity(bdev->bd_disk)) return -ERANGE; page = brd_insert_page(brd, sector); if (!page) return -ENOSPC; *kaddr = page_address(page); *pfn = page_to_pfn(page); return 0; /* * TODO: If size > PAGE_SIZE, we could look to see if the next page in * the file happens to be mapped to the next page of physical RAM. */ return PAGE_SIZE; } #endif Loading drivers/s390/block/dcssblk.c +9 −12 Original line number Diff line number Diff line Loading @@ -28,8 +28,8 @@ static int dcssblk_open(struct block_device *bdev, fmode_t mode); static void dcssblk_release(struct gendisk *disk, fmode_t mode); static void dcssblk_make_request(struct request_queue *q, struct bio *bio); static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn); static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn, long size); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; Loading Loading @@ -866,25 +866,22 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) bio_io_error(bio); } static int static long dcssblk_direct_access (struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn) void **kaddr, unsigned long *pfn, long size) { struct dcssblk_dev_info *dev_info; unsigned long pgoff; unsigned long offset, dev_sz; dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; if (secnum % (PAGE_SIZE/512)) return -EINVAL; pgoff = secnum / (PAGE_SIZE / 512); if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) return -ERANGE; *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE); dev_sz = dev_info->end - dev_info->start; offset = secnum * 512; *kaddr = (void *) (dev_info->start + offset); *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; return 0; return dev_sz - offset; } static void Loading fs/block_dev.c +40 −0 Original line number Diff line number Diff line Loading @@ -429,6 +429,46 @@ int bdev_write_page(struct block_device *bdev, sector_t sector, } EXPORT_SYMBOL_GPL(bdev_write_page); /** * bdev_direct_access() - Get the address for directly-accessibly memory * @bdev: The device containing the memory * @sector: The offset within the device * @addr: Where to put the address of the memory * @pfn: The Page Frame Number for the memory * @size: The number of bytes requested * * If a block device is made up of directly addressable memory, this function * will tell the caller the PFN and the address of the memory. The address * may be directly dereferenced within the kernel without the need to call * ioremap(), kmap() or similar. The PFN is suitable for inserting into * page tables. * * Return: negative errno if an error occurs, otherwise the number of bytes * accessible at this address. */ long bdev_direct_access(struct block_device *bdev, sector_t sector, void **addr, unsigned long *pfn, long size) { long avail; const struct block_device_operations *ops = bdev->bd_disk->fops; if (size < 0) return size; if (!ops->direct_access) return -EOPNOTSUPP; if ((sector + DIV_ROUND_UP(size, 512)) > part_nr_sects_read(bdev->bd_part)) return -ERANGE; sector += get_start_sect(bdev); if (sector % (PAGE_SIZE / 512)) return -EINVAL; avail = ops->direct_access(bdev, sector, addr, pfn, size); if (!avail) return -ERANGE; return min(avail, size); } EXPORT_SYMBOL_GPL(bdev_direct_access); /* * pseudo-fs */ Loading Loading
Documentation/filesystems/xip.txt +9 −6 Original line number Diff line number Diff line Loading @@ -28,12 +28,15 @@ Implementation Execute-in-place is implemented in three steps: block device operation, address space operation, and file operations. A block device operation named direct_access is used to retrieve a reference (pointer) to a block on-disk. The reference is supposed to be cpu-addressable, physical address and remain valid until the release operation is performed. A struct block_device reference is used to address the device, and a sector_t argument is used to identify the individual block. As an alternative, memory technology devices can be used for this. A block device operation named direct_access is used to translate the block device sector number to a page frame number (pfn) that identifies the physical page for the memory. It also returns a kernel virtual address that can be used to access the memory. The direct_access method takes a 'size' parameter that indicates the number of bytes being requested. The function should return the number of bytes that can be contiguously accessed at that offset. It may also return a negative errno if an error occurs. The block device operation is optional, these block devices support it as of today: Loading
arch/powerpc/sysdev/axonram.c +4 −13 Original line number Diff line number Diff line Loading @@ -139,26 +139,17 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) * axon_ram_direct_access - direct_access() method for block device * @device, @sector, @data: see block_device_operations method */ static int static long axon_ram_direct_access(struct block_device *device, sector_t sector, void **kaddr, unsigned long *pfn) void **kaddr, unsigned long *pfn, long size) { struct axon_ram_bank *bank = device->bd_disk->private_data; loff_t offset; offset = sector; if (device->bd_part != NULL) offset += device->bd_part->start_sect; offset <<= AXON_RAM_SECTOR_SHIFT; if (offset >= bank->size) { dev_err(&bank->device->dev, "Access outside of address space\n"); return -ERANGE; } loff_t offset = (loff_t)sector << AXON_RAM_SECTOR_SHIFT; *kaddr = (void *)(bank->ph_addr + offset); *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; return 0; return bank->size - offset; } static const struct block_device_operations axon_ram_devops = { Loading
drivers/block/brd.c +7 −7 Original line number Diff line number Diff line Loading @@ -370,25 +370,25 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector, } #ifdef CONFIG_BLK_DEV_XIP static int brd_direct_access(struct block_device *bdev, sector_t sector, void **kaddr, unsigned long *pfn) static long brd_direct_access(struct block_device *bdev, sector_t sector, void **kaddr, unsigned long *pfn, long size) { struct brd_device *brd = bdev->bd_disk->private_data; struct page *page; if (!brd) return -ENODEV; if (sector & (PAGE_SECTORS-1)) return -EINVAL; if (sector + PAGE_SECTORS > get_capacity(bdev->bd_disk)) return -ERANGE; page = brd_insert_page(brd, sector); if (!page) return -ENOSPC; *kaddr = page_address(page); *pfn = page_to_pfn(page); return 0; /* * TODO: If size > PAGE_SIZE, we could look to see if the next page in * the file happens to be mapped to the next page of physical RAM. */ return PAGE_SIZE; } #endif Loading
drivers/s390/block/dcssblk.c +9 −12 Original line number Diff line number Diff line Loading @@ -28,8 +28,8 @@ static int dcssblk_open(struct block_device *bdev, fmode_t mode); static void dcssblk_release(struct gendisk *disk, fmode_t mode); static void dcssblk_make_request(struct request_queue *q, struct bio *bio); static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn); static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn, long size); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; Loading Loading @@ -866,25 +866,22 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) bio_io_error(bio); } static int static long dcssblk_direct_access (struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn) void **kaddr, unsigned long *pfn, long size) { struct dcssblk_dev_info *dev_info; unsigned long pgoff; unsigned long offset, dev_sz; dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; if (secnum % (PAGE_SIZE/512)) return -EINVAL; pgoff = secnum / (PAGE_SIZE / 512); if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) return -ERANGE; *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE); dev_sz = dev_info->end - dev_info->start; offset = secnum * 512; *kaddr = (void *) (dev_info->start + offset); *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; return 0; return dev_sz - offset; } static void Loading
fs/block_dev.c +40 −0 Original line number Diff line number Diff line Loading @@ -429,6 +429,46 @@ int bdev_write_page(struct block_device *bdev, sector_t sector, } EXPORT_SYMBOL_GPL(bdev_write_page); /** * bdev_direct_access() - Get the address for directly-accessibly memory * @bdev: The device containing the memory * @sector: The offset within the device * @addr: Where to put the address of the memory * @pfn: The Page Frame Number for the memory * @size: The number of bytes requested * * If a block device is made up of directly addressable memory, this function * will tell the caller the PFN and the address of the memory. The address * may be directly dereferenced within the kernel without the need to call * ioremap(), kmap() or similar. The PFN is suitable for inserting into * page tables. * * Return: negative errno if an error occurs, otherwise the number of bytes * accessible at this address. */ long bdev_direct_access(struct block_device *bdev, sector_t sector, void **addr, unsigned long *pfn, long size) { long avail; const struct block_device_operations *ops = bdev->bd_disk->fops; if (size < 0) return size; if (!ops->direct_access) return -EOPNOTSUPP; if ((sector + DIV_ROUND_UP(size, 512)) > part_nr_sects_read(bdev->bd_part)) return -ERANGE; sector += get_start_sect(bdev); if (sector % (PAGE_SIZE / 512)) return -EINVAL; avail = ops->direct_access(bdev, sector, addr, pfn, size); if (!avail) return -ERANGE; return min(avail, size); } EXPORT_SYMBOL_GPL(bdev_direct_access); /* * pseudo-fs */ Loading