diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/hvc_iucv.c | 204 | ||||
-rw-r--r-- | drivers/char/ps3flash.c | 296 |
2 files changed, 325 insertions, 175 deletions
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 54481a887769..86105efb4eb6 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * This HVC device driver provides terminal access using | 4 | * This HVC device driver provides terminal access using |
5 | * z/VM IUCV communication paths. | 5 | * z/VM IUCV communication paths. |
6 | * | 6 | * |
7 | * Copyright IBM Corp. 2008 | 7 | * Copyright IBM Corp. 2008, 2009 |
8 | * | 8 | * |
9 | * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> | 9 | * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> |
10 | */ | 10 | */ |
@@ -15,6 +15,7 @@ | |||
15 | #include <asm/ebcdic.h> | 15 | #include <asm/ebcdic.h> |
16 | #include <linux/ctype.h> | 16 | #include <linux/ctype.h> |
17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | #include <linux/device.h> | ||
18 | #include <linux/init.h> | 19 | #include <linux/init.h> |
19 | #include <linux/mempool.h> | 20 | #include <linux/mempool.h> |
20 | #include <linux/moduleparam.h> | 21 | #include <linux/moduleparam.h> |
@@ -74,6 +75,7 @@ struct hvc_iucv_private { | |||
74 | wait_queue_head_t sndbuf_waitq; /* wait for send completion */ | 75 | wait_queue_head_t sndbuf_waitq; /* wait for send completion */ |
75 | struct list_head tty_outqueue; /* outgoing IUCV messages */ | 76 | struct list_head tty_outqueue; /* outgoing IUCV messages */ |
76 | struct list_head tty_inqueue; /* incoming IUCV messages */ | 77 | struct list_head tty_inqueue; /* incoming IUCV messages */ |
78 | struct device *dev; /* device structure */ | ||
77 | }; | 79 | }; |
78 | 80 | ||
79 | struct iucv_tty_buffer { | 81 | struct iucv_tty_buffer { |
@@ -542,7 +544,68 @@ static void flush_sndbuf_sync(struct hvc_iucv_private *priv) | |||
542 | 544 | ||
543 | if (sync_wait) | 545 | if (sync_wait) |
544 | wait_event_timeout(priv->sndbuf_waitq, | 546 | wait_event_timeout(priv->sndbuf_waitq, |
545 | tty_outqueue_empty(priv), HZ); | 547 | tty_outqueue_empty(priv), HZ/10); |
548 | } | ||
549 | |||
550 | /** | ||
551 | * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up | ||
552 | * @priv: Pointer to hvc_iucv_private structure | ||
553 | * | ||
554 | * This routine severs an existing IUCV communication path and hangs | ||
555 | * up the underlying HVC terminal device. | ||
556 | * The hang-up occurs only if an IUCV communication path is established; | ||
557 | * otherwise there is no need to hang up the terminal device. | ||
558 | * | ||
559 | * The IUCV HVC hang-up is separated into two steps: | ||
560 | * 1. After the IUCV path has been severed, the iucv_state is set to | ||
561 | * IUCV_SEVERED. | ||
562 | * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the | ||
563 | * IUCV_SEVERED state causes the tty hang-up in the HVC layer. | ||
564 | * | ||
565 | * If the tty has not yet been opened, clean up the hvc_iucv_private | ||
566 | * structure to allow re-connects. | ||
567 | * If the tty has been opened, let get_chars() return -EPIPE to signal | ||
568 | * the HVC layer to hang up the tty and, if so, wake up the HVC thread | ||
569 | * to call get_chars()... | ||
570 | * | ||
571 | * Special notes on hanging up a HVC terminal instantiated as console: | ||
572 | * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) | ||
573 | * 2. do_tty_hangup() calls tty->ops->close() for console_filp | ||
574 | * => no hangup notifier is called by HVC (default) | ||
575 | * 2. hvc_close() returns because of tty_hung_up_p(filp) | ||
576 | * => no delete notifier is called! | ||
577 | * Finally, the back-end is not being notified, thus, the tty session is | ||
578 | * kept active (TTY_OPEN) to be ready for re-connects. | ||
579 | * | ||
580 | * Locking: spin_lock(&priv->lock) w/o disabling bh | ||
581 | */ | ||
582 | static void hvc_iucv_hangup(struct hvc_iucv_private *priv) | ||
583 | { | ||
584 | struct iucv_path *path; | ||
585 | |||
586 | path = NULL; | ||
587 | spin_lock(&priv->lock); | ||
588 | if (priv->iucv_state == IUCV_CONNECTED) { | ||
589 | path = priv->path; | ||
590 | priv->path = NULL; | ||
591 | priv->iucv_state = IUCV_SEVERED; | ||
592 | if (priv->tty_state == TTY_CLOSED) | ||
593 | hvc_iucv_cleanup(priv); | ||
594 | else | ||
595 | /* console is special (see above) */ | ||
596 | if (priv->is_console) { | ||
597 | hvc_iucv_cleanup(priv); | ||
598 | priv->tty_state = TTY_OPENED; | ||
599 | } else | ||
600 | hvc_kick(); | ||
601 | } | ||
602 | spin_unlock(&priv->lock); | ||
603 | |||
604 | /* finally sever path (outside of priv->lock due to lock ordering) */ | ||
605 | if (path) { | ||
606 | iucv_path_sever(path, NULL); | ||
607 | iucv_path_free(path); | ||
608 | } | ||
546 | } | 609 | } |
547 | 610 | ||
548 | /** | 611 | /** |
@@ -735,11 +798,8 @@ out_path_handled: | |||
735 | * @ipuser: User specified data for this path | 798 | * @ipuser: User specified data for this path |
736 | * (AF_IUCV: port/service name and originator port) | 799 | * (AF_IUCV: port/service name and originator port) |
737 | * | 800 | * |
738 | * The function also severs the path (as required by the IUCV protocol) and | 801 | * This function calls the hvc_iucv_hangup() function for the |
739 | * sets the iucv state to IUCV_SEVERED for the associated struct | 802 | * respective IUCV HVC terminal. |
740 | * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty | ||
741 | * hangup (hvc_iucv_get_chars() / hvc_iucv_write()). | ||
742 | * If tty portion of the HVC is closed, clean up the outqueue. | ||
743 | * | 803 | * |
744 | * Locking: struct hvc_iucv_private->lock | 804 | * Locking: struct hvc_iucv_private->lock |
745 | */ | 805 | */ |
@@ -747,33 +807,7 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) | |||
747 | { | 807 | { |
748 | struct hvc_iucv_private *priv = path->private; | 808 | struct hvc_iucv_private *priv = path->private; |
749 | 809 | ||
750 | spin_lock(&priv->lock); | 810 | hvc_iucv_hangup(priv); |
751 | priv->iucv_state = IUCV_SEVERED; | ||
752 | |||
753 | /* If the tty has not yet been opened, clean up the hvc_iucv_private | ||
754 | * structure to allow re-connects. | ||
755 | * This is also done for our console device because console hangups | ||
756 | * are handled specially and no notifier is called by HVC. | ||
757 | * The tty session is active (TTY_OPEN) and ready for re-connects... | ||
758 | * | ||
759 | * If it has been opened, let get_chars() return -EPIPE to signal the | ||
760 | * HVC layer to hang up the tty. | ||
761 | * If so, we need to wake up the HVC thread to call get_chars()... | ||
762 | */ | ||
763 | priv->path = NULL; | ||
764 | if (priv->tty_state == TTY_CLOSED) | ||
765 | hvc_iucv_cleanup(priv); | ||
766 | else | ||
767 | if (priv->is_console) { | ||
768 | hvc_iucv_cleanup(priv); | ||
769 | priv->tty_state = TTY_OPENED; | ||
770 | } else | ||
771 | hvc_kick(); | ||
772 | spin_unlock(&priv->lock); | ||
773 | |||
774 | /* finally sever path (outside of priv->lock due to lock ordering) */ | ||
775 | iucv_path_sever(path, ipuser); | ||
776 | iucv_path_free(path); | ||
777 | } | 811 | } |
778 | 812 | ||
779 | /** | 813 | /** |
@@ -853,6 +887,37 @@ static void hvc_iucv_msg_complete(struct iucv_path *path, | |||
853 | destroy_tty_buffer_list(&list_remove); | 887 | destroy_tty_buffer_list(&list_remove); |
854 | } | 888 | } |
855 | 889 | ||
890 | /** | ||
891 | * hvc_iucv_pm_freeze() - Freeze PM callback | ||
892 | * @dev: IUVC HVC terminal device | ||
893 | * | ||
894 | * Sever an established IUCV communication path and | ||
895 | * trigger a hang-up of the underlying HVC terminal. | ||
896 | */ | ||
897 | static int hvc_iucv_pm_freeze(struct device *dev) | ||
898 | { | ||
899 | struct hvc_iucv_private *priv = dev_get_drvdata(dev); | ||
900 | |||
901 | local_bh_disable(); | ||
902 | hvc_iucv_hangup(priv); | ||
903 | local_bh_enable(); | ||
904 | |||
905 | return 0; | ||
906 | } | ||
907 | |||
908 | /** | ||
909 | * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback | ||
910 | * @dev: IUVC HVC terminal device | ||
911 | * | ||
912 | * Wake up the HVC thread to trigger hang-up and respective | ||
913 | * HVC back-end notifier invocations. | ||
914 | */ | ||
915 | static int hvc_iucv_pm_restore_thaw(struct device *dev) | ||
916 | { | ||
917 | hvc_kick(); | ||
918 | return 0; | ||
919 | } | ||
920 | |||
856 | 921 | ||
857 | /* HVC operations */ | 922 | /* HVC operations */ |
858 | static struct hv_ops hvc_iucv_ops = { | 923 | static struct hv_ops hvc_iucv_ops = { |
@@ -863,6 +928,20 @@ static struct hv_ops hvc_iucv_ops = { | |||
863 | .notifier_hangup = hvc_iucv_notifier_hangup, | 928 | .notifier_hangup = hvc_iucv_notifier_hangup, |
864 | }; | 929 | }; |
865 | 930 | ||
931 | /* Suspend / resume device operations */ | ||
932 | static struct dev_pm_ops hvc_iucv_pm_ops = { | ||
933 | .freeze = hvc_iucv_pm_freeze, | ||
934 | .thaw = hvc_iucv_pm_restore_thaw, | ||
935 | .restore = hvc_iucv_pm_restore_thaw, | ||
936 | }; | ||
937 | |||
938 | /* IUCV HVC device driver */ | ||
939 | static struct device_driver hvc_iucv_driver = { | ||
940 | .name = KMSG_COMPONENT, | ||
941 | .bus = &iucv_bus, | ||
942 | .pm = &hvc_iucv_pm_ops, | ||
943 | }; | ||
944 | |||
866 | /** | 945 | /** |
867 | * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance | 946 | * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance |
868 | * @id: hvc_iucv_table index | 947 | * @id: hvc_iucv_table index |
@@ -897,14 +976,12 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) | |||
897 | /* set console flag */ | 976 | /* set console flag */ |
898 | priv->is_console = is_console; | 977 | priv->is_console = is_console; |
899 | 978 | ||
900 | /* finally allocate hvc */ | 979 | /* allocate hvc device */ |
901 | priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ | 980 | priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ |
902 | HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); | 981 | HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); |
903 | if (IS_ERR(priv->hvc)) { | 982 | if (IS_ERR(priv->hvc)) { |
904 | rc = PTR_ERR(priv->hvc); | 983 | rc = PTR_ERR(priv->hvc); |
905 | free_page((unsigned long) priv->sndbuf); | 984 | goto out_error_hvc; |
906 | kfree(priv); | ||
907 | return rc; | ||
908 | } | 985 | } |
909 | 986 | ||
910 | /* notify HVC thread instead of using polling */ | 987 | /* notify HVC thread instead of using polling */ |
@@ -915,8 +992,45 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) | |||
915 | memcpy(priv->srv_name, name, 8); | 992 | memcpy(priv->srv_name, name, 8); |
916 | ASCEBC(priv->srv_name, 8); | 993 | ASCEBC(priv->srv_name, 8); |
917 | 994 | ||
995 | /* create and setup device */ | ||
996 | priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); | ||
997 | if (!priv->dev) { | ||
998 | rc = -ENOMEM; | ||
999 | goto out_error_dev; | ||
1000 | } | ||
1001 | dev_set_name(priv->dev, "hvc_iucv%d", id); | ||
1002 | dev_set_drvdata(priv->dev, priv); | ||
1003 | priv->dev->bus = &iucv_bus; | ||
1004 | priv->dev->parent = iucv_root; | ||
1005 | priv->dev->driver = &hvc_iucv_driver; | ||
1006 | priv->dev->release = (void (*)(struct device *)) kfree; | ||
1007 | rc = device_register(priv->dev); | ||
1008 | if (rc) { | ||
1009 | kfree(priv->dev); | ||
1010 | goto out_error_dev; | ||
1011 | } | ||
1012 | |||
918 | hvc_iucv_table[id] = priv; | 1013 | hvc_iucv_table[id] = priv; |
919 | return 0; | 1014 | return 0; |
1015 | |||
1016 | out_error_dev: | ||
1017 | hvc_remove(priv->hvc); | ||
1018 | out_error_hvc: | ||
1019 | free_page((unsigned long) priv->sndbuf); | ||
1020 | kfree(priv); | ||
1021 | |||
1022 | return rc; | ||
1023 | } | ||
1024 | |||
1025 | /** | ||
1026 | * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances | ||
1027 | */ | ||
1028 | static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) | ||
1029 | { | ||
1030 | hvc_remove(priv->hvc); | ||
1031 | device_unregister(priv->dev); | ||
1032 | free_page((unsigned long) priv->sndbuf); | ||
1033 | kfree(priv); | ||
920 | } | 1034 | } |
921 | 1035 | ||
922 | /** | 1036 | /** |
@@ -1109,6 +1223,11 @@ static int __init hvc_iucv_init(void) | |||
1109 | goto out_error; | 1223 | goto out_error; |
1110 | } | 1224 | } |
1111 | 1225 | ||
1226 | /* register IUCV HVC device driver */ | ||
1227 | rc = driver_register(&hvc_iucv_driver); | ||
1228 | if (rc) | ||
1229 | goto out_error; | ||
1230 | |||
1112 | /* parse hvc_iucv_allow string and create z/VM user ID filter list */ | 1231 | /* parse hvc_iucv_allow string and create z/VM user ID filter list */ |
1113 | if (hvc_iucv_filter_string) { | 1232 | if (hvc_iucv_filter_string) { |
1114 | rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); | 1233 | rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); |
@@ -1183,15 +1302,14 @@ out_error_iucv: | |||
1183 | iucv_unregister(&hvc_iucv_handler, 0); | 1302 | iucv_unregister(&hvc_iucv_handler, 0); |
1184 | out_error_hvc: | 1303 | out_error_hvc: |
1185 | for (i = 0; i < hvc_iucv_devices; i++) | 1304 | for (i = 0; i < hvc_iucv_devices; i++) |
1186 | if (hvc_iucv_table[i]) { | 1305 | if (hvc_iucv_table[i]) |
1187 | if (hvc_iucv_table[i]->hvc) | 1306 | hvc_iucv_destroy(hvc_iucv_table[i]); |
1188 | hvc_remove(hvc_iucv_table[i]->hvc); | ||
1189 | kfree(hvc_iucv_table[i]); | ||
1190 | } | ||
1191 | out_error_memory: | 1307 | out_error_memory: |
1192 | mempool_destroy(hvc_iucv_mempool); | 1308 | mempool_destroy(hvc_iucv_mempool); |
1193 | kmem_cache_destroy(hvc_iucv_buffer_cache); | 1309 | kmem_cache_destroy(hvc_iucv_buffer_cache); |
1194 | out_error: | 1310 | out_error: |
1311 | if (hvc_iucv_filter) | ||
1312 | kfree(hvc_iucv_filter); | ||
1195 | hvc_iucv_devices = 0; /* ensure that we do not provide any device */ | 1313 | hvc_iucv_devices = 0; /* ensure that we do not provide any device */ |
1196 | return rc; | 1314 | return rc; |
1197 | } | 1315 | } |
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index afbe45676d71..f424d394a286 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c | |||
@@ -33,48 +33,64 @@ | |||
33 | 33 | ||
34 | struct ps3flash_private { | 34 | struct ps3flash_private { |
35 | struct mutex mutex; /* Bounce buffer mutex */ | 35 | struct mutex mutex; /* Bounce buffer mutex */ |
36 | u64 chunk_sectors; | ||
37 | int tag; /* Start sector of buffer, -1 if invalid */ | ||
38 | bool dirty; | ||
36 | }; | 39 | }; |
37 | 40 | ||
38 | static struct ps3_storage_device *ps3flash_dev; | 41 | static struct ps3_storage_device *ps3flash_dev; |
39 | 42 | ||
40 | static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, | 43 | static int ps3flash_read_write_sectors(struct ps3_storage_device *dev, |
41 | u64 lpar, u64 start_sector, | 44 | u64 start_sector, int write) |
42 | u64 sectors, int write) | ||
43 | { | 45 | { |
44 | u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, | 46 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
47 | u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, | ||
48 | start_sector, priv->chunk_sectors, | ||
45 | write); | 49 | write); |
46 | if (res) { | 50 | if (res) { |
47 | dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, | 51 | dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, |
48 | __LINE__, write ? "write" : "read", res); | 52 | __LINE__, write ? "write" : "read", res); |
49 | return -EIO; | 53 | return -EIO; |
50 | } | 54 | } |
51 | return sectors; | 55 | return 0; |
52 | } | 56 | } |
53 | 57 | ||
54 | static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev, | 58 | static int ps3flash_writeback(struct ps3_storage_device *dev) |
55 | u64 start_sector, u64 sectors, | ||
56 | unsigned int sector_offset) | ||
57 | { | 59 | { |
58 | u64 max_sectors, lpar; | 60 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
61 | int res; | ||
59 | 62 | ||
60 | max_sectors = dev->bounce_size / dev->blk_size; | 63 | if (!priv->dirty || priv->tag < 0) |
61 | if (sectors > max_sectors) { | 64 | return 0; |
62 | dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %llu\n", | ||
63 | __func__, __LINE__, max_sectors); | ||
64 | sectors = max_sectors; | ||
65 | } | ||
66 | 65 | ||
67 | lpar = dev->bounce_lpar + sector_offset * dev->blk_size; | 66 | res = ps3flash_read_write_sectors(dev, priv->tag, 1); |
68 | return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, | 67 | if (res) |
69 | 0); | 68 | return res; |
69 | |||
70 | priv->dirty = false; | ||
71 | return 0; | ||
70 | } | 72 | } |
71 | 73 | ||
72 | static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev, | 74 | static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector) |
73 | u64 start_sector) | ||
74 | { | 75 | { |
75 | u64 sectors = dev->bounce_size / dev->blk_size; | 76 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
76 | return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector, | 77 | int res; |
77 | sectors, 1); | 78 | |
79 | if (start_sector == priv->tag) | ||
80 | return 0; | ||
81 | |||
82 | res = ps3flash_writeback(dev); | ||
83 | if (res) | ||
84 | return res; | ||
85 | |||
86 | priv->tag = -1; | ||
87 | |||
88 | res = ps3flash_read_write_sectors(dev, start_sector, 0); | ||
89 | if (res) | ||
90 | return res; | ||
91 | |||
92 | priv->tag = start_sector; | ||
93 | return 0; | ||
78 | } | 94 | } |
79 | 95 | ||
80 | static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) | 96 | static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) |
@@ -104,18 +120,19 @@ out: | |||
104 | return res; | 120 | return res; |
105 | } | 121 | } |
106 | 122 | ||
107 | static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, | 123 | static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, |
108 | loff_t *pos) | 124 | size_t count, loff_t *pos) |
109 | { | 125 | { |
110 | struct ps3_storage_device *dev = ps3flash_dev; | 126 | struct ps3_storage_device *dev = ps3flash_dev; |
111 | struct ps3flash_private *priv = dev->sbd.core.driver_data; | 127 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
112 | u64 size, start_sector, end_sector, offset; | 128 | u64 size, sector, offset; |
113 | ssize_t sectors_read; | 129 | int res; |
114 | size_t remaining, n; | 130 | size_t remaining, n; |
131 | const void *src; | ||
115 | 132 | ||
116 | dev_dbg(&dev->sbd.core, | 133 | dev_dbg(&dev->sbd.core, |
117 | "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", | 134 | "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n", |
118 | __func__, __LINE__, count, *pos, buf); | 135 | __func__, __LINE__, count, *pos, userbuf, kernelbuf); |
119 | 136 | ||
120 | size = dev->regions[dev->region_idx].size*dev->blk_size; | 137 | size = dev->regions[dev->region_idx].size*dev->blk_size; |
121 | if (*pos >= size || !count) | 138 | if (*pos >= size || !count) |
@@ -128,61 +145,63 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, | |||
128 | count = size - *pos; | 145 | count = size - *pos; |
129 | } | 146 | } |
130 | 147 | ||
131 | start_sector = *pos / dev->blk_size; | 148 | sector = *pos / dev->bounce_size * priv->chunk_sectors; |
132 | offset = *pos % dev->blk_size; | 149 | offset = *pos % dev->bounce_size; |
133 | end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size); | ||
134 | 150 | ||
135 | remaining = count; | 151 | remaining = count; |
136 | do { | 152 | do { |
153 | n = min_t(u64, remaining, dev->bounce_size - offset); | ||
154 | src = dev->bounce_buf + offset; | ||
155 | |||
137 | mutex_lock(&priv->mutex); | 156 | mutex_lock(&priv->mutex); |
138 | 157 | ||
139 | sectors_read = ps3flash_read_sectors(dev, start_sector, | 158 | res = ps3flash_fetch(dev, sector); |
140 | end_sector-start_sector, | 159 | if (res) |
141 | 0); | ||
142 | if (sectors_read < 0) { | ||
143 | mutex_unlock(&priv->mutex); | ||
144 | goto fail; | 160 | goto fail; |
145 | } | ||
146 | 161 | ||
147 | n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); | ||
148 | dev_dbg(&dev->sbd.core, | 162 | dev_dbg(&dev->sbd.core, |
149 | "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", | 163 | "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", |
150 | __func__, __LINE__, n, dev->bounce_buf+offset, buf); | 164 | __func__, __LINE__, n, src, userbuf, kernelbuf); |
151 | if (copy_to_user(buf, dev->bounce_buf+offset, n)) { | 165 | if (userbuf) { |
152 | mutex_unlock(&priv->mutex); | 166 | if (copy_to_user(userbuf, src, n)) { |
153 | sectors_read = -EFAULT; | 167 | res = -EFAULT; |
154 | goto fail; | 168 | goto fail; |
169 | } | ||
170 | userbuf += n; | ||
171 | } | ||
172 | if (kernelbuf) { | ||
173 | memcpy(kernelbuf, src, n); | ||
174 | kernelbuf += n; | ||
155 | } | 175 | } |
156 | 176 | ||
157 | mutex_unlock(&priv->mutex); | 177 | mutex_unlock(&priv->mutex); |
158 | 178 | ||
159 | *pos += n; | 179 | *pos += n; |
160 | buf += n; | ||
161 | remaining -= n; | 180 | remaining -= n; |
162 | start_sector += sectors_read; | 181 | sector += priv->chunk_sectors; |
163 | offset = 0; | 182 | offset = 0; |
164 | } while (remaining > 0); | 183 | } while (remaining > 0); |
165 | 184 | ||
166 | return count; | 185 | return count; |
167 | 186 | ||
168 | fail: | 187 | fail: |
169 | return sectors_read; | 188 | mutex_unlock(&priv->mutex); |
189 | return res; | ||
170 | } | 190 | } |
171 | 191 | ||
172 | static ssize_t ps3flash_write(struct file *file, const char __user *buf, | 192 | static ssize_t ps3flash_write(const char __user *userbuf, |
173 | size_t count, loff_t *pos) | 193 | const void *kernelbuf, size_t count, loff_t *pos) |
174 | { | 194 | { |
175 | struct ps3_storage_device *dev = ps3flash_dev; | 195 | struct ps3_storage_device *dev = ps3flash_dev; |
176 | struct ps3flash_private *priv = dev->sbd.core.driver_data; | 196 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
177 | u64 size, chunk_sectors, start_write_sector, end_write_sector, | 197 | u64 size, sector, offset; |
178 | end_read_sector, start_read_sector, head, tail, offset; | 198 | int res = 0; |
179 | ssize_t res; | ||
180 | size_t remaining, n; | 199 | size_t remaining, n; |
181 | unsigned int sec_off; | 200 | void *dst; |
182 | 201 | ||
183 | dev_dbg(&dev->sbd.core, | 202 | dev_dbg(&dev->sbd.core, |
184 | "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", | 203 | "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n", |
185 | __func__, __LINE__, count, *pos, buf); | 204 | __func__, __LINE__, count, *pos, userbuf, kernelbuf); |
186 | 205 | ||
187 | size = dev->regions[dev->region_idx].size*dev->blk_size; | 206 | size = dev->regions[dev->region_idx].size*dev->blk_size; |
188 | if (*pos >= size || !count) | 207 | if (*pos >= size || !count) |
@@ -195,89 +214,46 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, | |||
195 | count = size - *pos; | 214 | count = size - *pos; |
196 | } | 215 | } |
197 | 216 | ||
198 | chunk_sectors = dev->bounce_size / dev->blk_size; | 217 | sector = *pos / dev->bounce_size * priv->chunk_sectors; |
199 | |||
200 | start_write_sector = *pos / dev->bounce_size * chunk_sectors; | ||
201 | offset = *pos % dev->bounce_size; | 218 | offset = *pos % dev->bounce_size; |
202 | end_write_sector = DIV_ROUND_UP(*pos + count, dev->bounce_size) * | ||
203 | chunk_sectors; | ||
204 | |||
205 | end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size); | ||
206 | start_read_sector = (*pos + count) / dev->blk_size; | ||
207 | |||
208 | /* | ||
209 | * As we have to write in 256 KiB chunks, while we can read in blk_size | ||
210 | * (usually 512 bytes) chunks, we perform the following steps: | ||
211 | * 1. Read from start_write_sector to end_read_sector ("head") | ||
212 | * 2. Read from start_read_sector to end_write_sector ("tail") | ||
213 | * 3. Copy data to buffer | ||
214 | * 4. Write from start_write_sector to end_write_sector | ||
215 | * All of this is complicated by using only one 256 KiB bounce buffer. | ||
216 | */ | ||
217 | |||
218 | head = end_read_sector - start_write_sector; | ||
219 | tail = end_write_sector - start_read_sector; | ||
220 | 219 | ||
221 | remaining = count; | 220 | remaining = count; |
222 | do { | 221 | do { |
222 | n = min_t(u64, remaining, dev->bounce_size - offset); | ||
223 | dst = dev->bounce_buf + offset; | ||
224 | |||
223 | mutex_lock(&priv->mutex); | 225 | mutex_lock(&priv->mutex); |
224 | 226 | ||
225 | if (end_read_sector >= start_read_sector) { | 227 | if (n != dev->bounce_size) |
226 | /* Merge head and tail */ | 228 | res = ps3flash_fetch(dev, sector); |
227 | dev_dbg(&dev->sbd.core, | 229 | else if (sector != priv->tag) |
228 | "Merged head and tail: %llu sectors at %llu\n", | 230 | res = ps3flash_writeback(dev); |
229 | chunk_sectors, start_write_sector); | 231 | if (res) |
230 | res = ps3flash_read_sectors(dev, start_write_sector, | 232 | goto fail; |
231 | chunk_sectors, 0); | 233 | |
232 | if (res < 0) | 234 | dev_dbg(&dev->sbd.core, |
235 | "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", | ||
236 | __func__, __LINE__, n, userbuf, kernelbuf, dst); | ||
237 | if (userbuf) { | ||
238 | if (copy_from_user(dst, userbuf, n)) { | ||
239 | res = -EFAULT; | ||
233 | goto fail; | 240 | goto fail; |
234 | } else { | ||
235 | if (head) { | ||
236 | /* Read head */ | ||
237 | dev_dbg(&dev->sbd.core, | ||
238 | "head: %llu sectors at %llu\n", head, | ||
239 | start_write_sector); | ||
240 | res = ps3flash_read_sectors(dev, | ||
241 | start_write_sector, | ||
242 | head, 0); | ||
243 | if (res < 0) | ||
244 | goto fail; | ||
245 | } | ||
246 | if (start_read_sector < | ||
247 | start_write_sector+chunk_sectors) { | ||
248 | /* Read tail */ | ||
249 | dev_dbg(&dev->sbd.core, | ||
250 | "tail: %llu sectors at %llu\n", tail, | ||
251 | start_read_sector); | ||
252 | sec_off = start_read_sector-start_write_sector; | ||
253 | res = ps3flash_read_sectors(dev, | ||
254 | start_read_sector, | ||
255 | tail, sec_off); | ||
256 | if (res < 0) | ||
257 | goto fail; | ||
258 | } | 241 | } |
242 | userbuf += n; | ||
259 | } | 243 | } |
260 | 244 | if (kernelbuf) { | |
261 | n = min_t(u64, remaining, dev->bounce_size-offset); | 245 | memcpy(dst, kernelbuf, n); |
262 | dev_dbg(&dev->sbd.core, | 246 | kernelbuf += n; |
263 | "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", | ||
264 | __func__, __LINE__, n, buf, dev->bounce_buf+offset); | ||
265 | if (copy_from_user(dev->bounce_buf+offset, buf, n)) { | ||
266 | res = -EFAULT; | ||
267 | goto fail; | ||
268 | } | 247 | } |
269 | 248 | ||
270 | res = ps3flash_write_chunk(dev, start_write_sector); | 249 | priv->tag = sector; |
271 | if (res < 0) | 250 | priv->dirty = true; |
272 | goto fail; | ||
273 | 251 | ||
274 | mutex_unlock(&priv->mutex); | 252 | mutex_unlock(&priv->mutex); |
275 | 253 | ||
276 | *pos += n; | 254 | *pos += n; |
277 | buf += n; | ||
278 | remaining -= n; | 255 | remaining -= n; |
279 | start_write_sector += chunk_sectors; | 256 | sector += priv->chunk_sectors; |
280 | head = 0; | ||
281 | offset = 0; | 257 | offset = 0; |
282 | } while (remaining > 0); | 258 | } while (remaining > 0); |
283 | 259 | ||
@@ -288,6 +264,51 @@ fail: | |||
288 | return res; | 264 | return res; |
289 | } | 265 | } |
290 | 266 | ||
267 | static ssize_t ps3flash_user_read(struct file *file, char __user *buf, | ||
268 | size_t count, loff_t *pos) | ||
269 | { | ||
270 | return ps3flash_read(buf, NULL, count, pos); | ||
271 | } | ||
272 | |||
273 | static ssize_t ps3flash_user_write(struct file *file, const char __user *buf, | ||
274 | size_t count, loff_t *pos) | ||
275 | { | ||
276 | return ps3flash_write(buf, NULL, count, pos); | ||
277 | } | ||
278 | |||
279 | static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos) | ||
280 | { | ||
281 | return ps3flash_read(NULL, buf, count, &pos); | ||
282 | } | ||
283 | |||
284 | static ssize_t ps3flash_kernel_write(const void *buf, size_t count, | ||
285 | loff_t pos) | ||
286 | { | ||
287 | ssize_t res; | ||
288 | int wb; | ||
289 | |||
290 | res = ps3flash_write(NULL, buf, count, &pos); | ||
291 | if (res < 0) | ||
292 | return res; | ||
293 | |||
294 | /* Make kernel writes synchronous */ | ||
295 | wb = ps3flash_writeback(ps3flash_dev); | ||
296 | if (wb) | ||
297 | return wb; | ||
298 | |||
299 | return res; | ||
300 | } | ||
301 | |||
302 | static int ps3flash_flush(struct file *file, fl_owner_t id) | ||
303 | { | ||
304 | return ps3flash_writeback(ps3flash_dev); | ||
305 | } | ||
306 | |||
307 | static int ps3flash_fsync(struct file *file, struct dentry *dentry, | ||
308 | int datasync) | ||
309 | { | ||
310 | return ps3flash_writeback(ps3flash_dev); | ||
311 | } | ||
291 | 312 | ||
292 | static irqreturn_t ps3flash_interrupt(int irq, void *data) | 313 | static irqreturn_t ps3flash_interrupt(int irq, void *data) |
293 | { | 314 | { |
@@ -312,12 +333,18 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data) | |||
312 | return IRQ_HANDLED; | 333 | return IRQ_HANDLED; |
313 | } | 334 | } |
314 | 335 | ||
315 | |||
316 | static const struct file_operations ps3flash_fops = { | 336 | static const struct file_operations ps3flash_fops = { |
317 | .owner = THIS_MODULE, | 337 | .owner = THIS_MODULE, |
318 | .llseek = ps3flash_llseek, | 338 | .llseek = ps3flash_llseek, |
319 | .read = ps3flash_read, | 339 | .read = ps3flash_user_read, |
320 | .write = ps3flash_write, | 340 | .write = ps3flash_user_write, |
341 | .flush = ps3flash_flush, | ||
342 | .fsync = ps3flash_fsync, | ||
343 | }; | ||
344 | |||
345 | static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { | ||
346 | .read = ps3flash_kernel_read, | ||
347 | .write = ps3flash_kernel_write, | ||
321 | }; | 348 | }; |
322 | 349 | ||
323 | static struct miscdevice ps3flash_misc = { | 350 | static struct miscdevice ps3flash_misc = { |
@@ -366,11 +393,13 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) | |||
366 | goto fail; | 393 | goto fail; |
367 | } | 394 | } |
368 | 395 | ||
369 | dev->sbd.core.driver_data = priv; | 396 | ps3_system_bus_set_drvdata(&dev->sbd, priv); |
370 | mutex_init(&priv->mutex); | 397 | mutex_init(&priv->mutex); |
398 | priv->tag = -1; | ||
371 | 399 | ||
372 | dev->bounce_size = ps3flash_bounce_buffer.size; | 400 | dev->bounce_size = ps3flash_bounce_buffer.size; |
373 | dev->bounce_buf = ps3flash_bounce_buffer.address; | 401 | dev->bounce_buf = ps3flash_bounce_buffer.address; |
402 | priv->chunk_sectors = dev->bounce_size / dev->blk_size; | ||
374 | 403 | ||
375 | error = ps3stor_setup(dev, ps3flash_interrupt); | 404 | error = ps3stor_setup(dev, ps3flash_interrupt); |
376 | if (error) | 405 | if (error) |
@@ -386,13 +415,15 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) | |||
386 | 415 | ||
387 | dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", | 416 | dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", |
388 | __func__, __LINE__, ps3flash_misc.minor); | 417 | __func__, __LINE__, ps3flash_misc.minor); |
418 | |||
419 | ps3_os_area_flash_register(&ps3flash_kernel_ops); | ||
389 | return 0; | 420 | return 0; |
390 | 421 | ||
391 | fail_teardown: | 422 | fail_teardown: |
392 | ps3stor_teardown(dev); | 423 | ps3stor_teardown(dev); |
393 | fail_free_priv: | 424 | fail_free_priv: |
394 | kfree(priv); | 425 | kfree(priv); |
395 | dev->sbd.core.driver_data = NULL; | 426 | ps3_system_bus_set_drvdata(&dev->sbd, NULL); |
396 | fail: | 427 | fail: |
397 | ps3flash_dev = NULL; | 428 | ps3flash_dev = NULL; |
398 | return error; | 429 | return error; |
@@ -402,10 +433,11 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev) | |||
402 | { | 433 | { |
403 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); | 434 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); |
404 | 435 | ||
436 | ps3_os_area_flash_register(NULL); | ||
405 | misc_deregister(&ps3flash_misc); | 437 | misc_deregister(&ps3flash_misc); |
406 | ps3stor_teardown(dev); | 438 | ps3stor_teardown(dev); |
407 | kfree(dev->sbd.core.driver_data); | 439 | kfree(ps3_system_bus_get_drvdata(&dev->sbd)); |
408 | dev->sbd.core.driver_data = NULL; | 440 | ps3_system_bus_set_drvdata(&dev->sbd, NULL); |
409 | ps3flash_dev = NULL; | 441 | ps3flash_dev = NULL; |
410 | return 0; | 442 | return 0; |
411 | } | 443 | } |