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