xref: /openbmc/linux/drivers/s390/char/sclp_sdias.c (revision 9ac8d3fb)
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 *cbsccb;
70 
71 	cbsccb = (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 sclp_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 		return rc;
244 	init_waitqueue_head(&sdias_wq);
245 	TRACE("init done\n");
246 	return 0;
247 }
248 
249 void __exit sclp_sdias_exit(void)
250 {
251 	debug_unregister(sdias_dbf);
252 	sclp_unregister(&sclp_sdias_register);
253 }
254