File Index
Files and directories
Sign in
FILE

sclp_ftp.c

File preview with syntax highlighting.

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