diff options
author | Jens Axboe <axboe@fb.com> | 2015-01-13 23:58:45 -0500 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2015-01-13 23:58:45 -0500 |
commit | d4119ee0e1aa2b74e5e367cbc915e79db7b9e271 (patch) | |
tree | dae4e7bebdda0db62a00c5bb3d054a6018d68444 | |
parent | 91117a20245b59f70b563523edbf998a62fc6383 (diff) | |
parent | dd22f551ac0ad366f92f601835f6623b83adc331 (diff) |
Merge branch 'for-3.20/core' into for-3.20/drivers
-rw-r--r-- | Documentation/filesystems/xip.txt | 15 | ||||
-rw-r--r-- | arch/powerpc/sysdev/axonram.c | 17 | ||||
-rw-r--r-- | drivers/block/brd.c | 14 | ||||
-rw-r--r-- | drivers/s390/block/dcssblk.c | 21 | ||||
-rw-r--r-- | fs/block_dev.c | 40 | ||||
-rw-r--r-- | fs/ext2/xip.c | 31 | ||||
-rw-r--r-- | include/linux/blkdev.h | 6 |
7 files changed, 86 insertions, 58 deletions
diff --git a/Documentation/filesystems/xip.txt b/Documentation/filesystems/xip.txt index 0466ee569278..b77472949ede 100644 --- a/Documentation/filesystems/xip.txt +++ b/Documentation/filesystems/xip.txt | |||
@@ -28,12 +28,15 @@ Implementation | |||
28 | Execute-in-place is implemented in three steps: block device operation, | 28 | Execute-in-place is implemented in three steps: block device operation, |
29 | address space operation, and file operations. | 29 | address space operation, and file operations. |
30 | 30 | ||
31 | A block device operation named direct_access is used to retrieve a | 31 | A block device operation named direct_access is used to translate the |
32 | reference (pointer) to a block on-disk. The reference is supposed to be | 32 | block device sector number to a page frame number (pfn) that identifies |
33 | cpu-addressable, physical address and remain valid until the release operation | 33 | the physical page for the memory. It also returns a kernel virtual |
34 | is performed. A struct block_device reference is used to address the device, | 34 | address that can be used to access the memory. |
35 | and a sector_t argument is used to identify the individual block. As an | 35 | |
36 | alternative, memory technology devices can be used for this. | 36 | The direct_access method takes a 'size' parameter that indicates the |
37 | number of bytes being requested. The function should return the number | ||
38 | of bytes that can be contiguously accessed at that offset. It may also | ||
39 | return a negative errno if an error occurs. | ||
37 | 40 | ||
38 | The block device operation is optional, these block devices support it as of | 41 | The block device operation is optional, these block devices support it as of |
39 | today: | 42 | today: |
diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index 367533bb3d48..ee90db17b097 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c | |||
@@ -139,26 +139,17 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) | |||
139 | * axon_ram_direct_access - direct_access() method for block device | 139 | * axon_ram_direct_access - direct_access() method for block device |
140 | * @device, @sector, @data: see block_device_operations method | 140 | * @device, @sector, @data: see block_device_operations method |
141 | */ | 141 | */ |
142 | static int | 142 | static long |
143 | axon_ram_direct_access(struct block_device *device, sector_t sector, | 143 | axon_ram_direct_access(struct block_device *device, sector_t sector, |
144 | void **kaddr, unsigned long *pfn) | 144 | void **kaddr, unsigned long *pfn, long size) |
145 | { | 145 | { |
146 | struct axon_ram_bank *bank = device->bd_disk->private_data; | 146 | struct axon_ram_bank *bank = device->bd_disk->private_data; |
147 | loff_t offset; | 147 | loff_t offset = (loff_t)sector << AXON_RAM_SECTOR_SHIFT; |
148 | |||
149 | offset = sector; | ||
150 | if (device->bd_part != NULL) | ||
151 | offset += device->bd_part->start_sect; | ||
152 | offset <<= AXON_RAM_SECTOR_SHIFT; | ||
153 | if (offset >= bank->size) { | ||
154 | dev_err(&bank->device->dev, "Access outside of address space\n"); | ||
155 | return -ERANGE; | ||
156 | } | ||
157 | 148 | ||
158 | *kaddr = (void *)(bank->ph_addr + offset); | 149 | *kaddr = (void *)(bank->ph_addr + offset); |
159 | *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; | 150 | *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; |
160 | 151 | ||
161 | return 0; | 152 | return bank->size - offset; |
162 | } | 153 | } |
163 | 154 | ||
164 | static const struct block_device_operations axon_ram_devops = { | 155 | static const struct block_device_operations axon_ram_devops = { |
diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 3598110d2cef..89e90ec52f28 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c | |||
@@ -370,25 +370,25 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector, | |||
370 | } | 370 | } |
371 | 371 | ||
372 | #ifdef CONFIG_BLK_DEV_XIP | 372 | #ifdef CONFIG_BLK_DEV_XIP |
373 | static int brd_direct_access(struct block_device *bdev, sector_t sector, | 373 | static long brd_direct_access(struct block_device *bdev, sector_t sector, |
374 | void **kaddr, unsigned long *pfn) | 374 | void **kaddr, unsigned long *pfn, long size) |
375 | { | 375 | { |
376 | struct brd_device *brd = bdev->bd_disk->private_data; | 376 | struct brd_device *brd = bdev->bd_disk->private_data; |
377 | struct page *page; | 377 | struct page *page; |
378 | 378 | ||
379 | if (!brd) | 379 | if (!brd) |
380 | return -ENODEV; | 380 | return -ENODEV; |
381 | if (sector & (PAGE_SECTORS-1)) | ||
382 | return -EINVAL; | ||
383 | if (sector + PAGE_SECTORS > get_capacity(bdev->bd_disk)) | ||
384 | return -ERANGE; | ||
385 | page = brd_insert_page(brd, sector); | 381 | page = brd_insert_page(brd, sector); |
386 | if (!page) | 382 | if (!page) |
387 | return -ENOSPC; | 383 | return -ENOSPC; |
388 | *kaddr = page_address(page); | 384 | *kaddr = page_address(page); |
389 | *pfn = page_to_pfn(page); | 385 | *pfn = page_to_pfn(page); |
390 | 386 | ||
391 | return 0; | 387 | /* |
388 | * TODO: If size > PAGE_SIZE, we could look to see if the next page in | ||
389 | * the file happens to be mapped to the next page of physical RAM. | ||
390 | */ | ||
391 | return PAGE_SIZE; | ||
392 | } | 392 | } |
393 | #endif | 393 | #endif |
394 | 394 | ||
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index b550c8c8d010..31d6884f3351 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c | |||
@@ -28,8 +28,8 @@ | |||
28 | static int dcssblk_open(struct block_device *bdev, fmode_t mode); | 28 | static int dcssblk_open(struct block_device *bdev, fmode_t mode); |
29 | static void dcssblk_release(struct gendisk *disk, fmode_t mode); | 29 | static void dcssblk_release(struct gendisk *disk, fmode_t mode); |
30 | static void dcssblk_make_request(struct request_queue *q, struct bio *bio); | 30 | static void dcssblk_make_request(struct request_queue *q, struct bio *bio); |
31 | static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, | 31 | static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum, |
32 | void **kaddr, unsigned long *pfn); | 32 | void **kaddr, unsigned long *pfn, long size); |
33 | 33 | ||
34 | static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; | 34 | static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; |
35 | 35 | ||
@@ -866,25 +866,22 @@ fail: | |||
866 | bio_io_error(bio); | 866 | bio_io_error(bio); |
867 | } | 867 | } |
868 | 868 | ||
869 | static int | 869 | static long |
870 | dcssblk_direct_access (struct block_device *bdev, sector_t secnum, | 870 | dcssblk_direct_access (struct block_device *bdev, sector_t secnum, |
871 | void **kaddr, unsigned long *pfn) | 871 | void **kaddr, unsigned long *pfn, long size) |
872 | { | 872 | { |
873 | struct dcssblk_dev_info *dev_info; | 873 | struct dcssblk_dev_info *dev_info; |
874 | unsigned long pgoff; | 874 | unsigned long offset, dev_sz; |
875 | 875 | ||
876 | dev_info = bdev->bd_disk->private_data; | 876 | dev_info = bdev->bd_disk->private_data; |
877 | if (!dev_info) | 877 | if (!dev_info) |
878 | return -ENODEV; | 878 | return -ENODEV; |
879 | if (secnum % (PAGE_SIZE/512)) | 879 | dev_sz = dev_info->end - dev_info->start; |
880 | return -EINVAL; | 880 | offset = secnum * 512; |
881 | pgoff = secnum / (PAGE_SIZE / 512); | 881 | *kaddr = (void *) (dev_info->start + offset); |
882 | if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) | ||
883 | return -ERANGE; | ||
884 | *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE); | ||
885 | *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; | 882 | *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; |
886 | 883 | ||
887 | return 0; | 884 | return dev_sz - offset; |
888 | } | 885 | } |
889 | 886 | ||
890 | static void | 887 | static void |
diff --git a/fs/block_dev.c b/fs/block_dev.c index b48c41bf0f86..f314c2c0567d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -429,6 +429,46 @@ int bdev_write_page(struct block_device *bdev, sector_t sector, | |||
429 | } | 429 | } |
430 | EXPORT_SYMBOL_GPL(bdev_write_page); | 430 | EXPORT_SYMBOL_GPL(bdev_write_page); |
431 | 431 | ||
432 | /** | ||
433 | * bdev_direct_access() - Get the address for directly-accessibly memory | ||
434 | * @bdev: The device containing the memory | ||
435 | * @sector: The offset within the device | ||
436 | * @addr: Where to put the address of the memory | ||
437 | * @pfn: The Page Frame Number for the memory | ||
438 | * @size: The number of bytes requested | ||
439 | * | ||
440 | * If a block device is made up of directly addressable memory, this function | ||
441 | * will tell the caller the PFN and the address of the memory. The address | ||
442 | * may be directly dereferenced within the kernel without the need to call | ||
443 | * ioremap(), kmap() or similar. The PFN is suitable for inserting into | ||
444 | * page tables. | ||
445 | * | ||
446 | * Return: negative errno if an error occurs, otherwise the number of bytes | ||
447 | * accessible at this address. | ||
448 | */ | ||
449 | long bdev_direct_access(struct block_device *bdev, sector_t sector, | ||
450 | void **addr, unsigned long *pfn, long size) | ||
451 | { | ||
452 | long avail; | ||
453 | const struct block_device_operations *ops = bdev->bd_disk->fops; | ||
454 | |||
455 | if (size < 0) | ||
456 | return size; | ||
457 | if (!ops->direct_access) | ||
458 | return -EOPNOTSUPP; | ||
459 | if ((sector + DIV_ROUND_UP(size, 512)) > | ||
460 | part_nr_sects_read(bdev->bd_part)) | ||
461 | return -ERANGE; | ||
462 | sector += get_start_sect(bdev); | ||
463 | if (sector % (PAGE_SIZE / 512)) | ||
464 | return -EINVAL; | ||
465 | avail = ops->direct_access(bdev, sector, addr, pfn, size); | ||
466 | if (!avail) | ||
467 | return -ERANGE; | ||
468 | return min(avail, size); | ||
469 | } | ||
470 | EXPORT_SYMBOL_GPL(bdev_direct_access); | ||
471 | |||
432 | /* | 472 | /* |
433 | * pseudo-fs | 473 | * pseudo-fs |
434 | */ | 474 | */ |
diff --git a/fs/ext2/xip.c b/fs/ext2/xip.c index e98171a11cfe..bbc5fec6ff7f 100644 --- a/fs/ext2/xip.c +++ b/fs/ext2/xip.c | |||
@@ -13,18 +13,12 @@ | |||
13 | #include "ext2.h" | 13 | #include "ext2.h" |
14 | #include "xip.h" | 14 | #include "xip.h" |
15 | 15 | ||
16 | static inline int | 16 | static inline long __inode_direct_access(struct inode *inode, sector_t block, |
17 | __inode_direct_access(struct inode *inode, sector_t block, | 17 | void **kaddr, unsigned long *pfn, long size) |
18 | void **kaddr, unsigned long *pfn) | ||
19 | { | 18 | { |
20 | struct block_device *bdev = inode->i_sb->s_bdev; | 19 | struct block_device *bdev = inode->i_sb->s_bdev; |
21 | const struct block_device_operations *ops = bdev->bd_disk->fops; | 20 | sector_t sector = block * (PAGE_SIZE / 512); |
22 | sector_t sector; | 21 | return bdev_direct_access(bdev, sector, kaddr, pfn, size); |
23 | |||
24 | sector = block * (PAGE_SIZE / 512); /* ext2 block to bdev sector */ | ||
25 | |||
26 | BUG_ON(!ops->direct_access); | ||
27 | return ops->direct_access(bdev, sector, kaddr, pfn); | ||
28 | } | 22 | } |
29 | 23 | ||
30 | static inline int | 24 | static inline int |
@@ -53,12 +47,13 @@ ext2_clear_xip_target(struct inode *inode, sector_t block) | |||
53 | { | 47 | { |
54 | void *kaddr; | 48 | void *kaddr; |
55 | unsigned long pfn; | 49 | unsigned long pfn; |
56 | int rc; | 50 | long size; |
57 | 51 | ||
58 | rc = __inode_direct_access(inode, block, &kaddr, &pfn); | 52 | size = __inode_direct_access(inode, block, &kaddr, &pfn, PAGE_SIZE); |
59 | if (!rc) | 53 | if (size < 0) |
60 | clear_page(kaddr); | 54 | return size; |
61 | return rc; | 55 | clear_page(kaddr); |
56 | return 0; | ||
62 | } | 57 | } |
63 | 58 | ||
64 | void ext2_xip_verify_sb(struct super_block *sb) | 59 | void ext2_xip_verify_sb(struct super_block *sb) |
@@ -77,7 +72,7 @@ void ext2_xip_verify_sb(struct super_block *sb) | |||
77 | int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create, | 72 | int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create, |
78 | void **kmem, unsigned long *pfn) | 73 | void **kmem, unsigned long *pfn) |
79 | { | 74 | { |
80 | int rc; | 75 | long rc; |
81 | sector_t block; | 76 | sector_t block; |
82 | 77 | ||
83 | /* first, retrieve the sector number */ | 78 | /* first, retrieve the sector number */ |
@@ -86,6 +81,6 @@ int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create, | |||
86 | return rc; | 81 | return rc; |
87 | 82 | ||
88 | /* retrieve address of the target data */ | 83 | /* retrieve address of the target data */ |
89 | rc = __inode_direct_access(mapping->host, block, kmem, pfn); | 84 | rc = __inode_direct_access(mapping->host, block, kmem, pfn, PAGE_SIZE); |
90 | return rc; | 85 | return (rc < 0) ? rc : 0; |
91 | } | 86 | } |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 92f4b4b288dd..e9086be6d9a0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -1601,8 +1601,8 @@ struct block_device_operations { | |||
1601 | int (*rw_page)(struct block_device *, sector_t, struct page *, int rw); | 1601 | int (*rw_page)(struct block_device *, sector_t, struct page *, int rw); |
1602 | int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); | 1602 | int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); |
1603 | int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); | 1603 | int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); |
1604 | int (*direct_access) (struct block_device *, sector_t, | 1604 | long (*direct_access)(struct block_device *, sector_t, |
1605 | void **, unsigned long *); | 1605 | void **, unsigned long *pfn, long size); |
1606 | unsigned int (*check_events) (struct gendisk *disk, | 1606 | unsigned int (*check_events) (struct gendisk *disk, |
1607 | unsigned int clearing); | 1607 | unsigned int clearing); |
1608 | /* ->media_changed() is DEPRECATED, use ->check_events() instead */ | 1608 | /* ->media_changed() is DEPRECATED, use ->check_events() instead */ |
@@ -1620,6 +1620,8 @@ extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int, | |||
1620 | extern int bdev_read_page(struct block_device *, sector_t, struct page *); | 1620 | extern int bdev_read_page(struct block_device *, sector_t, struct page *); |
1621 | extern int bdev_write_page(struct block_device *, sector_t, struct page *, | 1621 | extern int bdev_write_page(struct block_device *, sector_t, struct page *, |
1622 | struct writeback_control *); | 1622 | struct writeback_control *); |
1623 | extern long bdev_direct_access(struct block_device *, sector_t, void **addr, | ||
1624 | unsigned long *pfn, long size); | ||
1623 | #else /* CONFIG_BLOCK */ | 1625 | #else /* CONFIG_BLOCK */ |
1624 | 1626 | ||
1625 | struct block_device; | 1627 | struct block_device; |