diff options
Diffstat (limited to 'drivers/s390/char/sclp_sdias.c')
-rw-r--r-- | drivers/s390/char/sclp_sdias.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c new file mode 100644 index 000000000000..52283daddaef --- /dev/null +++ b/drivers/s390/char/sclp_sdias.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Sclp "store data in absolut storage" | ||
3 | * | ||
4 | * Copyright IBM Corp. 2003,2007 | ||
5 | * Author(s): Michael Holzheu | ||
6 | */ | ||
7 | |||
8 | #include <linux/sched.h> | ||
9 | #include <asm/sclp.h> | ||
10 | #include <asm/debug.h> | ||
11 | #include <asm/ipl.h> | ||
12 | #include "sclp.h" | ||
13 | #include "sclp_rw.h" | ||
14 | |||
15 | #define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) | ||
16 | #define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x ) | ||
17 | |||
18 | #define SDIAS_RETRIES 300 | ||
19 | #define SDIAS_SLEEP_TICKS 50 | ||
20 | |||
21 | #define EQ_STORE_DATA 0x0 | ||
22 | #define EQ_SIZE 0x1 | ||
23 | #define DI_FCP_DUMP 0x0 | ||
24 | #define ASA_SIZE_32 0x0 | ||
25 | #define ASA_SIZE_64 0x1 | ||
26 | #define EVSTATE_ALL_STORED 0x0 | ||
27 | #define EVSTATE_NO_DATA 0x3 | ||
28 | #define EVSTATE_PART_STORED 0x10 | ||
29 | |||
30 | static struct debug_info *sdias_dbf; | ||
31 | |||
32 | static struct sclp_register sclp_sdias_register = { | ||
33 | .send_mask = EVTYP_SDIAS_MASK, | ||
34 | }; | ||
35 | |||
36 | struct sdias_evbuf { | ||
37 | struct evbuf_header hdr; | ||
38 | u8 event_qual; | ||
39 | u8 data_id; | ||
40 | u64 reserved2; | ||
41 | u32 event_id; | ||
42 | u16 reserved3; | ||
43 | u8 asa_size; | ||
44 | u8 event_status; | ||
45 | u32 reserved4; | ||
46 | u32 blk_cnt; | ||
47 | u64 asa; | ||
48 | u32 reserved5; | ||
49 | u32 fbn; | ||
50 | u32 reserved6; | ||
51 | u32 lbn; | ||
52 | u16 reserved7; | ||
53 | u16 dbs; | ||
54 | } __attribute__((packed)); | ||
55 | |||
56 | struct sdias_sccb { | ||
57 | struct sccb_header hdr; | ||
58 | struct sdias_evbuf evbuf; | ||
59 | } __attribute__((packed)); | ||
60 | |||
61 | static struct sdias_sccb sccb __attribute__((aligned(4096))); | ||
62 | |||
63 | static int sclp_req_done; | ||
64 | static wait_queue_head_t sdias_wq; | ||
65 | static DEFINE_MUTEX(sdias_mutex); | ||
66 | |||
67 | static void sdias_callback(struct sclp_req *request, void *data) | ||
68 | { | ||
69 | struct sdias_sccb *sccb; | ||
70 | |||
71 | sccb = (struct sdias_sccb *) request->sccb; | ||
72 | sclp_req_done = 1; | ||
73 | wake_up(&sdias_wq); /* Inform caller, that request is complete */ | ||
74 | TRACE("callback done\n"); | ||
75 | } | ||
76 | |||
77 | static int sdias_sclp_send(struct sclp_req *req) | ||
78 | { | ||
79 | int retries; | ||
80 | int rc; | ||
81 | |||
82 | for (retries = SDIAS_RETRIES; retries; retries--) { | ||
83 | sclp_req_done = 0; | ||
84 | TRACE("add request\n"); | ||
85 | rc = sclp_add_request(req); | ||
86 | if (rc) { | ||
87 | /* not initiated, wait some time and retry */ | ||
88 | set_current_state(TASK_INTERRUPTIBLE); | ||
89 | TRACE("add request failed: rc = %i\n",rc); | ||
90 | schedule_timeout(SDIAS_SLEEP_TICKS); | ||
91 | continue; | ||
92 | } | ||
93 | /* initiated, wait for completion of service call */ | ||
94 | wait_event(sdias_wq, (sclp_req_done == 1)); | ||
95 | if (req->status == SCLP_REQ_FAILED) { | ||
96 | TRACE("sclp request failed\n"); | ||
97 | rc = -EIO; | ||
98 | continue; | ||
99 | } | ||
100 | TRACE("request done\n"); | ||
101 | break; | ||
102 | } | ||
103 | return rc; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Get number of blocks (4K) available in the HSA | ||
108 | */ | ||
109 | int sclp_sdias_blk_count(void) | ||
110 | { | ||
111 | struct sclp_req request; | ||
112 | int rc; | ||
113 | |||
114 | mutex_lock(&sdias_mutex); | ||
115 | |||
116 | memset(&sccb, 0, sizeof(sccb)); | ||
117 | memset(&request, 0, sizeof(request)); | ||
118 | |||
119 | sccb.hdr.length = sizeof(sccb); | ||
120 | sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); | ||
121 | sccb.evbuf.hdr.type = EVTYP_SDIAS; | ||
122 | sccb.evbuf.event_qual = EQ_SIZE; | ||
123 | sccb.evbuf.data_id = DI_FCP_DUMP; | ||
124 | sccb.evbuf.event_id = 4712; | ||
125 | sccb.evbuf.dbs = 1; | ||
126 | |||
127 | request.sccb = &sccb; | ||
128 | request.command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
129 | request.status = SCLP_REQ_FILLED; | ||
130 | request.callback = sdias_callback; | ||
131 | |||
132 | rc = sdias_sclp_send(&request); | ||
133 | if (rc) { | ||
134 | ERROR_MSG("sclp_send failed for get_nr_blocks\n"); | ||
135 | goto out; | ||
136 | } | ||
137 | if (sccb.hdr.response_code != 0x0020) { | ||
138 | TRACE("send failed: %x\n", sccb.hdr.response_code); | ||
139 | rc = -EIO; | ||
140 | goto out; | ||
141 | } | ||
142 | |||
143 | switch (sccb.evbuf.event_status) { | ||
144 | case 0: | ||
145 | rc = sccb.evbuf.blk_cnt; | ||
146 | break; | ||
147 | default: | ||
148 | ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status); | ||
149 | rc = -EIO; | ||
150 | goto out; | ||
151 | } | ||
152 | TRACE("%i blocks\n", rc); | ||
153 | out: | ||
154 | mutex_unlock(&sdias_mutex); | ||
155 | return rc; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Copy from HSA to absolute storage (not reentrant): | ||
160 | * | ||
161 | * @dest : Address of buffer where data should be copied | ||
162 | * @start_blk: Start Block (beginning with 1) | ||
163 | * @nr_blks : Number of 4K blocks to copy | ||
164 | * | ||
165 | * Return Value: 0 : Requested 'number' of blocks of data copied | ||
166 | * <0: ERROR - negative event status | ||
167 | */ | ||
168 | int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) | ||
169 | { | ||
170 | struct sclp_req request; | ||
171 | int rc; | ||
172 | |||
173 | mutex_lock(&sdias_mutex); | ||
174 | |||
175 | memset(&sccb, 0, sizeof(sccb)); | ||
176 | memset(&request, 0, sizeof(request)); | ||
177 | |||
178 | sccb.hdr.length = sizeof(sccb); | ||
179 | sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); | ||
180 | sccb.evbuf.hdr.type = EVTYP_SDIAS; | ||
181 | sccb.evbuf.hdr.flags = 0; | ||
182 | sccb.evbuf.event_qual = EQ_STORE_DATA; | ||
183 | sccb.evbuf.data_id = DI_FCP_DUMP; | ||
184 | sccb.evbuf.event_id = 4712; | ||
185 | #ifdef __s390x__ | ||
186 | sccb.evbuf.asa_size = ASA_SIZE_64; | ||
187 | #else | ||
188 | sccb.evbuf.asa_size = ASA_SIZE_32; | ||
189 | #endif | ||
190 | sccb.evbuf.event_status = 0; | ||
191 | sccb.evbuf.blk_cnt = nr_blks; | ||
192 | sccb.evbuf.asa = (unsigned long)dest; | ||
193 | sccb.evbuf.fbn = start_blk; | ||
194 | sccb.evbuf.lbn = 0; | ||
195 | sccb.evbuf.dbs = 1; | ||
196 | |||
197 | request.sccb = &sccb; | ||
198 | request.command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
199 | request.status = SCLP_REQ_FILLED; | ||
200 | request.callback = sdias_callback; | ||
201 | |||
202 | rc = sdias_sclp_send(&request); | ||
203 | if (rc) { | ||
204 | ERROR_MSG("sclp_send failed: %x\n", rc); | ||
205 | goto out; | ||
206 | } | ||
207 | if (sccb.hdr.response_code != 0x0020) { | ||
208 | TRACE("copy failed: %x\n", sccb.hdr.response_code); | ||
209 | rc = -EIO; | ||
210 | goto out; | ||
211 | } | ||
212 | |||
213 | switch (sccb.evbuf.event_status) { | ||
214 | case EVSTATE_ALL_STORED: | ||
215 | TRACE("all stored\n"); | ||
216 | case EVSTATE_PART_STORED: | ||
217 | TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); | ||
218 | break; | ||
219 | case EVSTATE_NO_DATA: | ||
220 | TRACE("no data\n"); | ||
221 | default: | ||
222 | ERROR_MSG("Error from SCLP while copying hsa. " | ||
223 | "Event status = %x\n", | ||
224 | sccb.evbuf.event_status); | ||
225 | rc = -EIO; | ||
226 | } | ||
227 | out: | ||
228 | mutex_unlock(&sdias_mutex); | ||
229 | return rc; | ||
230 | } | ||
231 | |||
232 | int __init sdias_init(void) | ||
233 | { | ||
234 | int rc; | ||
235 | |||
236 | if (ipl_info.type != IPL_TYPE_FCP_DUMP) | ||
237 | return 0; | ||
238 | sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); | ||
239 | debug_register_view(sdias_dbf, &debug_sprintf_view); | ||
240 | debug_set_level(sdias_dbf, 6); | ||
241 | rc = sclp_register(&sclp_sdias_register); | ||
242 | if (rc) { | ||
243 | ERROR_MSG("sclp register failed\n"); | ||
244 | return rc; | ||
245 | } | ||
246 | init_waitqueue_head(&sdias_wq); | ||
247 | TRACE("init done\n"); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | void __exit sdias_exit(void) | ||
252 | { | ||
253 | debug_unregister(sdias_dbf); | ||
254 | sclp_unregister(&sclp_sdias_register); | ||
255 | } | ||