Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / s390 / char / sclp_ftp.c
1 /*
2  *    SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
3  *
4  *    Copyright IBM Corp. 2013
5  *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6  *
7  */
8
9 #define KMSG_COMPONENT "hmcdrv"
10 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12 #include <linux/kernel.h>
13 #include <linux/mm.h>
14 #include <linux/slab.h>
15 #include <linux/io.h>
16 #include <linux/wait.h>
17 #include <linux/string.h>
18 #include <linux/jiffies.h>
19 #include <asm/sysinfo.h>
20 #include <asm/ebcdic.h>
21
22 #include "sclp.h"
23 #include "sclp_diag.h"
24 #include "sclp_ftp.h"
25
26 static DECLARE_COMPLETION(sclp_ftp_rx_complete);
27 static u8 sclp_ftp_ldflg;
28 static u64 sclp_ftp_fsize;
29 static u64 sclp_ftp_length;
30
31 /**
32  * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback
33  */
34 static void sclp_ftp_txcb(struct sclp_req *req, void *data)
35 {
36         struct completion *completion = data;
37
38 #ifdef DEBUG
39         pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
40                  req->sccb, 24, req->sccb);
41 #endif
42         complete(completion);
43 }
44
45 /**
46  * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback
47  */
48 static void sclp_ftp_rxcb(struct evbuf_header *evbuf)
49 {
50         struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
51
52         /*
53          * Check for Diagnostic Test FTP Service
54          */
55         if (evbuf->type != EVTYP_DIAG_TEST ||
56             diag->route != SCLP_DIAG_FTP_ROUTE ||
57             diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
58             evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
59                 return;
60
61 #ifdef DEBUG
62         pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
63                  evbuf, 24, evbuf);
64 #endif
65
66         /*
67          * Because the event buffer is located in a page which is owned
68          * by the SCLP core, all data of interest must be copied. The
69          * error indication is in 'sclp_ftp_ldflg'
70          */
71         sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
72         sclp_ftp_fsize = diag->mdd.ftp.fsize;
73         sclp_ftp_length = diag->mdd.ftp.length;
74
75         complete(&sclp_ftp_rx_complete);
76 }
77
78 /**
79  * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request
80  * @ftp: pointer to FTP descriptor
81  *
82  * Return: 0 on success, else a (negative) error code
83  */
84 static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
85 {
86         struct completion completion;
87         struct sclp_diag_sccb *sccb;
88         struct sclp_req *req;
89         size_t len;
90         int rc;
91
92         req = kzalloc(sizeof(*req), GFP_KERNEL);
93         sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
94         if (!req || !sccb) {
95                 rc = -ENOMEM;
96                 goto out_free;
97         }
98
99         sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
100                 sizeof(struct sccb_header);
101         sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
102         sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
103         sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */
104         sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
105         sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
106         sccb->evbuf.mdd.ftp.srcflg = 0;
107         sccb->evbuf.mdd.ftp.pgsize = 0;
108         sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
109         sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
110         sccb->evbuf.mdd.ftp.fsize = 0;
111         sccb->evbuf.mdd.ftp.cmd = ftp->id;
112         sccb->evbuf.mdd.ftp.offset = ftp->ofs;
113         sccb->evbuf.mdd.ftp.length = ftp->len;
114         sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
115
116         len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
117                       HMCDRV_FTP_FIDENT_MAX);
118         if (len >= HMCDRV_FTP_FIDENT_MAX) {
119                 rc = -EINVAL;
120                 goto out_free;
121         }
122
123         req->command = SCLP_CMDW_WRITE_EVENT_DATA;
124         req->sccb = sccb;
125         req->status = SCLP_REQ_FILLED;
126         req->callback = sclp_ftp_txcb;
127         req->callback_data = &completion;
128
129         init_completion(&completion);
130
131         rc = sclp_add_request(req);
132         if (rc)
133                 goto out_free;
134
135         /* Wait for end of ftp sclp command. */
136         wait_for_completion(&completion);
137
138 #ifdef DEBUG
139         pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
140                  sccb->hdr.response_code, sccb->evbuf.hdr.flags);
141 #endif
142
143         /*
144          * Check if sclp accepted the request. The data transfer runs
145          * asynchronously and the completion is indicated with an
146          * sclp ET7 event.
147          */
148         if (req->status != SCLP_REQ_DONE ||
149             (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */
150             (sccb->hdr.response_code & 0xffU) != 0x20U) {
151                 rc = -EIO;
152         }
153
154 out_free:
155         free_page((unsigned long) sccb);
156         kfree(req);
157         return rc;
158 }
159
160 /**
161  * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command
162  * @ftp: pointer to FTP command specification
163  * @fsize: return of file size (or NULL if undesirable)
164  *
165  * Attention: Notice that this function is not reentrant - so the caller
166  * must ensure locking.
167  *
168  * Return: number of bytes read/written or a (negative) error code
169  */
170 ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
171 {
172         ssize_t len;
173 #ifdef DEBUG
174         unsigned long start_jiffies;
175
176         pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
177                  ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
178         start_jiffies = jiffies;
179 #endif
180
181         init_completion(&sclp_ftp_rx_complete);
182
183         /* Start ftp sclp command. */
184         len = sclp_ftp_et7(ftp);
185         if (len)
186                 goto out_unlock;
187
188         /*
189          * There is no way to cancel the sclp ET7 request, the code
190          * needs to wait unconditionally until the transfer is complete.
191          */
192         wait_for_completion(&sclp_ftp_rx_complete);
193
194 #ifdef DEBUG
195         pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
196                  (jiffies - start_jiffies) * 1000 / HZ);
197         pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
198                  sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
199 #endif
200
201         switch (sclp_ftp_ldflg) {
202         case SCLP_DIAG_FTP_OK:
203                 len = sclp_ftp_length;
204                 if (fsize)
205                         *fsize = sclp_ftp_fsize;
206                 break;
207         case SCLP_DIAG_FTP_LDNPERM:
208                 len = -EPERM;
209                 break;
210         case SCLP_DIAG_FTP_LDRUNS:
211                 len = -EBUSY;
212                 break;
213         case SCLP_DIAG_FTP_LDFAIL:
214                 len = -ENOENT;
215                 break;
216         default:
217                 len = -EIO;
218                 break;
219         }
220
221 out_unlock:
222         return len;
223 }
224
225 /*
226  * ET7 event listener
227  */
228 static struct sclp_register sclp_ftp_event = {
229         .send_mask = EVTYP_DIAG_TEST_MASK,    /* want tx events */
230         .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */
231         .receiver_fn = sclp_ftp_rxcb,         /* async callback (rx) */
232         .state_change_fn = NULL,
233         .pm_event_fn = NULL,
234 };
235
236 /**
237  * sclp_ftp_startup() - startup of FTP services, when running on LPAR
238  */
239 int sclp_ftp_startup(void)
240 {
241 #ifdef DEBUG
242         unsigned long info;
243 #endif
244         int rc;
245
246         rc = sclp_register(&sclp_ftp_event);
247         if (rc)
248                 return rc;
249
250 #ifdef DEBUG
251         info = get_zeroed_page(GFP_KERNEL);
252
253         if (info != 0) {
254                 struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
255
256                 if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */
257                         info222->name[sizeof(info222->name) - 1] = '\0';
258                         EBCASC_500(info222->name, sizeof(info222->name) - 1);
259                         pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
260                                  info222->lpar_number, info222->name);
261                 }
262
263                 free_page(info);
264         }
265 #endif  /* DEBUG */
266         return 0;
267 }
268
269 /**
270  * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR
271  */
272 void sclp_ftp_shutdown(void)
273 {
274         sclp_unregister(&sclp_ftp_event);
275 }