diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/block/dcssblk.c | 44 |
1 files changed, 40 insertions, 4 deletions
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 16ab8d363ac6..6bc27d52326f 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c | |||
@@ -35,14 +35,17 @@ | |||
35 | static int dcssblk_open(struct inode *inode, struct file *filp); | 35 | static int dcssblk_open(struct inode *inode, struct file *filp); |
36 | static int dcssblk_release(struct inode *inode, struct file *filp); | 36 | static int dcssblk_release(struct inode *inode, struct file *filp); |
37 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); | 37 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); |
38 | static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, | ||
39 | unsigned long *data); | ||
38 | 40 | ||
39 | static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; | 41 | static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; |
40 | 42 | ||
41 | static int dcssblk_major; | 43 | static int dcssblk_major; |
42 | static struct block_device_operations dcssblk_devops = { | 44 | static struct block_device_operations dcssblk_devops = { |
43 | .owner = THIS_MODULE, | 45 | .owner = THIS_MODULE, |
44 | .open = dcssblk_open, | 46 | .open = dcssblk_open, |
45 | .release = dcssblk_release, | 47 | .release = dcssblk_release, |
48 | .direct_access = dcssblk_direct_access, | ||
46 | }; | 49 | }; |
47 | 50 | ||
48 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, | 51 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, |
@@ -641,6 +644,20 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) | |||
641 | /* Request beyond end of DCSS segment. */ | 644 | /* Request beyond end of DCSS segment. */ |
642 | goto fail; | 645 | goto fail; |
643 | } | 646 | } |
647 | /* verify data transfer direction */ | ||
648 | if (dev_info->is_shared) { | ||
649 | switch (dev_info->segment_type) { | ||
650 | case SEG_TYPE_SR: | ||
651 | case SEG_TYPE_ER: | ||
652 | case SEG_TYPE_SC: | ||
653 | /* cannot write to these segments */ | ||
654 | if (bio_data_dir(bio) == WRITE) { | ||
655 | PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); | ||
656 | goto fail; | ||
657 | } | ||
658 | } | ||
659 | } | ||
660 | |||
644 | index = (bio->bi_sector >> 3); | 661 | index = (bio->bi_sector >> 3); |
645 | bio_for_each_segment(bvec, bio, i) { | 662 | bio_for_each_segment(bvec, bio, i) { |
646 | page_addr = (unsigned long) | 663 | page_addr = (unsigned long) |
@@ -661,7 +678,26 @@ dcssblk_make_request(request_queue_t *q, struct bio *bio) | |||
661 | bio_endio(bio, bytes_done, 0); | 678 | bio_endio(bio, bytes_done, 0); |
662 | return 0; | 679 | return 0; |
663 | fail: | 680 | fail: |
664 | bio_io_error(bio, bytes_done); | 681 | bio_io_error(bio, bio->bi_size); |
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static int | ||
686 | dcssblk_direct_access (struct block_device *bdev, sector_t secnum, | ||
687 | unsigned long *data) | ||
688 | { | ||
689 | struct dcssblk_dev_info *dev_info; | ||
690 | unsigned long pgoff; | ||
691 | |||
692 | dev_info = bdev->bd_disk->private_data; | ||
693 | if (!dev_info) | ||
694 | return -ENODEV; | ||
695 | if (secnum % (PAGE_SIZE/512)) | ||
696 | return -EINVAL; | ||
697 | pgoff = secnum / (PAGE_SIZE / 512); | ||
698 | if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) | ||
699 | return -ERANGE; | ||
700 | *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); | ||
665 | return 0; | 701 | return 0; |
666 | } | 702 | } |
667 | 703 | ||