1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * SCLP "store data in absolute storage" |
4 | * |
5 | * Copyright IBM Corp. 2003, 2013 |
6 | * Author(s): Michael Holzheu |
7 | */ |
8 | |
9 | #define KMSG_COMPONENT "sclp_sdias" |
10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
11 | |
12 | #include <linux/completion.h> |
13 | #include <linux/sched.h> |
14 | #include <asm/sclp.h> |
15 | #include <asm/debug.h> |
16 | #include <asm/ipl.h> |
17 | |
18 | #include "sclp_sdias.h" |
19 | #include "sclp.h" |
20 | #include "sclp_rw.h" |
21 | |
22 | #define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) |
23 | |
24 | #define SDIAS_RETRIES 300 |
25 | |
26 | static struct debug_info *sdias_dbf; |
27 | |
28 | static struct sclp_register sclp_sdias_register = { |
29 | .send_mask = EVTYP_SDIAS_MASK, |
30 | }; |
31 | |
32 | static struct sdias_sccb *sclp_sdias_sccb; |
33 | static struct sdias_evbuf sdias_evbuf; |
34 | |
35 | static DECLARE_COMPLETION(evbuf_accepted); |
36 | static DECLARE_COMPLETION(evbuf_done); |
37 | static DEFINE_MUTEX(sdias_mutex); |
38 | |
39 | /* |
40 | * Called by SCLP base when read event data has been completed (async mode only) |
41 | */ |
42 | static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf) |
43 | { |
44 | memcpy(&sdias_evbuf, evbuf, |
45 | min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length)); |
46 | complete(&evbuf_done); |
47 | TRACE("sclp_sdias_receiver_fn done\n" ); |
48 | } |
49 | |
50 | /* |
51 | * Called by SCLP base when sdias event has been accepted |
52 | */ |
53 | static void sdias_callback(struct sclp_req *request, void *data) |
54 | { |
55 | complete(&evbuf_accepted); |
56 | TRACE("callback done\n" ); |
57 | } |
58 | |
59 | static int sdias_sclp_send(struct sclp_req *req) |
60 | { |
61 | struct sdias_sccb *sccb = sclp_sdias_sccb; |
62 | int retries; |
63 | int rc; |
64 | |
65 | for (retries = SDIAS_RETRIES; retries; retries--) { |
66 | TRACE("add request\n" ); |
67 | rc = sclp_add_request(req); |
68 | if (rc) { |
69 | /* not initiated, wait some time and retry */ |
70 | set_current_state(TASK_INTERRUPTIBLE); |
71 | TRACE("add request failed: rc = %i\n" ,rc); |
72 | schedule_timeout(timeout: msecs_to_jiffies(m: 500)); |
73 | continue; |
74 | } |
75 | /* initiated, wait for completion of service call */ |
76 | wait_for_completion(&evbuf_accepted); |
77 | if (req->status == SCLP_REQ_FAILED) { |
78 | TRACE("sclp request failed\n" ); |
79 | continue; |
80 | } |
81 | /* if not accepted, retry */ |
82 | if (!(sccb->evbuf.hdr.flags & 0x80)) { |
83 | TRACE("sclp request failed: flags=%x\n" , |
84 | sccb->evbuf.hdr.flags); |
85 | continue; |
86 | } |
87 | /* |
88 | * for the sync interface the response is in the initial sccb |
89 | */ |
90 | if (!sclp_sdias_register.receiver_fn) { |
91 | memcpy(&sdias_evbuf, &sccb->evbuf, sizeof(sdias_evbuf)); |
92 | TRACE("sync request done\n" ); |
93 | return 0; |
94 | } |
95 | /* otherwise we wait for completion */ |
96 | wait_for_completion(&evbuf_done); |
97 | TRACE("request done\n" ); |
98 | return 0; |
99 | } |
100 | return -EIO; |
101 | } |
102 | |
103 | /* |
104 | * Get number of blocks (4K) available in the HSA |
105 | */ |
106 | int sclp_sdias_blk_count(void) |
107 | { |
108 | struct sdias_sccb *sccb = sclp_sdias_sccb; |
109 | struct sclp_req request; |
110 | int rc; |
111 | |
112 | mutex_lock(&sdias_mutex); |
113 | |
114 | memset(sccb, 0, sizeof(*sccb)); |
115 | memset(&request, 0, sizeof(request)); |
116 | |
117 | sccb->hdr.length = sizeof(*sccb); |
118 | sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); |
119 | sccb->evbuf.hdr.type = EVTYP_SDIAS; |
120 | sccb->evbuf.event_qual = SDIAS_EQ_SIZE; |
121 | sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; |
122 | sccb->evbuf.event_id = 4712; |
123 | sccb->evbuf.dbs = 1; |
124 | |
125 | request.sccb = sccb; |
126 | request.command = SCLP_CMDW_WRITE_EVENT_DATA; |
127 | request.status = SCLP_REQ_FILLED; |
128 | request.callback = sdias_callback; |
129 | |
130 | rc = sdias_sclp_send(req: &request); |
131 | if (rc) { |
132 | pr_err("sclp_send failed for get_nr_blocks\n" ); |
133 | goto out; |
134 | } |
135 | if (sccb->hdr.response_code != 0x0020) { |
136 | TRACE("send failed: %x\n" , sccb->hdr.response_code); |
137 | rc = -EIO; |
138 | goto out; |
139 | } |
140 | |
141 | switch (sdias_evbuf.event_status) { |
142 | case 0: |
143 | rc = sdias_evbuf.blk_cnt; |
144 | break; |
145 | default: |
146 | pr_err("SCLP error: %x\n" , sdias_evbuf.event_status); |
147 | rc = -EIO; |
148 | goto out; |
149 | } |
150 | TRACE("%i blocks\n" , rc); |
151 | out: |
152 | mutex_unlock(lock: &sdias_mutex); |
153 | return rc; |
154 | } |
155 | |
156 | /* |
157 | * Copy from HSA to absolute storage (not reentrant): |
158 | * |
159 | * @dest : Address of buffer where data should be copied |
160 | * @start_blk: Start Block (beginning with 1) |
161 | * @nr_blks : Number of 4K blocks to copy |
162 | * |
163 | * Return Value: 0 : Requested 'number' of blocks of data copied |
164 | * <0: ERROR - negative event status |
165 | */ |
166 | int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) |
167 | { |
168 | struct sdias_sccb *sccb = sclp_sdias_sccb; |
169 | struct sclp_req request; |
170 | int rc; |
171 | |
172 | mutex_lock(&sdias_mutex); |
173 | |
174 | memset(sccb, 0, sizeof(*sccb)); |
175 | memset(&request, 0, sizeof(request)); |
176 | |
177 | sccb->hdr.length = sizeof(*sccb); |
178 | sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); |
179 | sccb->evbuf.hdr.type = EVTYP_SDIAS; |
180 | sccb->evbuf.hdr.flags = 0; |
181 | sccb->evbuf.event_qual = SDIAS_EQ_STORE_DATA; |
182 | sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; |
183 | sccb->evbuf.event_id = 4712; |
184 | sccb->evbuf.asa_size = SDIAS_ASA_SIZE_64; |
185 | sccb->evbuf.event_status = 0; |
186 | sccb->evbuf.blk_cnt = nr_blks; |
187 | sccb->evbuf.asa = __pa(dest); |
188 | sccb->evbuf.fbn = start_blk; |
189 | sccb->evbuf.lbn = 0; |
190 | sccb->evbuf.dbs = 1; |
191 | |
192 | request.sccb = sccb; |
193 | request.command = SCLP_CMDW_WRITE_EVENT_DATA; |
194 | request.status = SCLP_REQ_FILLED; |
195 | request.callback = sdias_callback; |
196 | |
197 | rc = sdias_sclp_send(req: &request); |
198 | if (rc) { |
199 | pr_err("sclp_send failed: %x\n" , rc); |
200 | goto out; |
201 | } |
202 | if (sccb->hdr.response_code != 0x0020) { |
203 | TRACE("copy failed: %x\n" , sccb->hdr.response_code); |
204 | rc = -EIO; |
205 | goto out; |
206 | } |
207 | |
208 | switch (sdias_evbuf.event_status) { |
209 | case SDIAS_EVSTATE_ALL_STORED: |
210 | TRACE("all stored\n" ); |
211 | break; |
212 | case SDIAS_EVSTATE_PART_STORED: |
213 | TRACE("part stored: %i\n" , sdias_evbuf.blk_cnt); |
214 | break; |
215 | case SDIAS_EVSTATE_NO_DATA: |
216 | TRACE("no data\n" ); |
217 | fallthrough; |
218 | default: |
219 | pr_err("Error from SCLP while copying hsa. Event status = %x\n" , |
220 | sdias_evbuf.event_status); |
221 | rc = -EIO; |
222 | } |
223 | out: |
224 | mutex_unlock(lock: &sdias_mutex); |
225 | return rc; |
226 | } |
227 | |
228 | static int __init sclp_sdias_register_check(void) |
229 | { |
230 | int rc; |
231 | |
232 | rc = sclp_register(reg: &sclp_sdias_register); |
233 | if (rc) |
234 | return rc; |
235 | if (sclp_sdias_blk_count() == 0) { |
236 | sclp_unregister(reg: &sclp_sdias_register); |
237 | return -ENODEV; |
238 | } |
239 | return 0; |
240 | } |
241 | |
242 | static int __init sclp_sdias_init_sync(void) |
243 | { |
244 | TRACE("Try synchronous mode\n" ); |
245 | sclp_sdias_register.receive_mask = 0; |
246 | sclp_sdias_register.receiver_fn = NULL; |
247 | return sclp_sdias_register_check(); |
248 | } |
249 | |
250 | static int __init sclp_sdias_init_async(void) |
251 | { |
252 | TRACE("Try asynchronous mode\n" ); |
253 | sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK; |
254 | sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn; |
255 | return sclp_sdias_register_check(); |
256 | } |
257 | |
258 | int __init sclp_sdias_init(void) |
259 | { |
260 | if (!is_ipl_type_dump()) |
261 | return 0; |
262 | sclp_sdias_sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA); |
263 | BUG_ON(!sclp_sdias_sccb); |
264 | sdias_dbf = debug_register("dump_sdias" , 4, 1, 4 * sizeof(long)); |
265 | debug_register_view(sdias_dbf, &debug_sprintf_view); |
266 | debug_set_level(sdias_dbf, 6); |
267 | if (sclp_sdias_init_sync() == 0) |
268 | goto out; |
269 | if (sclp_sdias_init_async() == 0) |
270 | goto out; |
271 | TRACE("init failed\n" ); |
272 | free_page((unsigned long) sclp_sdias_sccb); |
273 | return -ENODEV; |
274 | out: |
275 | TRACE("init done\n" ); |
276 | return 0; |
277 | } |
278 | |