]>
Commit | Line | Data |
---|---|---|
411ed322 MH |
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 = { | |
6d4740c8 | 33 | .send_mask = EVTYP_SDIAS_MASK, |
411ed322 MH |
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); | |
6d4740c8 | 121 | sccb.evbuf.hdr.type = EVTYP_SDIAS; |
411ed322 MH |
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); | |
6d4740c8 | 180 | sccb.evbuf.hdr.type = EVTYP_SDIAS; |
411ed322 MH |
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 | } |