278 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * SCLP "store data in absolute storage"4 *5 * Copyright IBM Corp. 2003, 20136 * Author(s): Michael Holzheu7 */8 9#define KMSG_COMPONENT "sclp_sdias"10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt11 12#include <linux/completion.h>13#include <linux/sched.h>14#include <asm/sclp.h>15#include <asm/debug.h>16#include <asm/ipl.h>17 18#include "sclp_sdias.h"19#include "sclp.h"20#include "sclp_rw.h"21 22#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)23 24#define SDIAS_RETRIES 30025 26static struct debug_info *sdias_dbf;27 28static struct sclp_register sclp_sdias_register = {29 .send_mask = EVTYP_SDIAS_MASK,30};31 32static struct sdias_sccb *sclp_sdias_sccb;33static struct sdias_evbuf sdias_evbuf;34 35static DECLARE_COMPLETION(evbuf_accepted);36static DECLARE_COMPLETION(evbuf_done);37static DEFINE_MUTEX(sdias_mutex);38 39/*40 * Called by SCLP base when read event data has been completed (async mode only)41 */42static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf)43{44 memcpy(&sdias_evbuf, evbuf,45 min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length));46 complete(&evbuf_done);47 TRACE("sclp_sdias_receiver_fn done\n");48}49 50/*51 * Called by SCLP base when sdias event has been accepted52 */53static void sdias_callback(struct sclp_req *request, void *data)54{55 complete(&evbuf_accepted);56 TRACE("callback done\n");57}58 59static int sdias_sclp_send(struct sclp_req *req)60{61 struct sdias_sccb *sccb = sclp_sdias_sccb;62 int retries;63 int rc;64 65 for (retries = SDIAS_RETRIES; retries; retries--) {66 TRACE("add request\n");67 rc = sclp_add_request(req);68 if (rc) {69 /* not initiated, wait some time and retry */70 set_current_state(TASK_INTERRUPTIBLE);71 TRACE("add request failed: rc = %i\n",rc);72 schedule_timeout(msecs_to_jiffies(500));73 continue;74 }75 /* initiated, wait for completion of service call */76 wait_for_completion(&evbuf_accepted);77 if (req->status == SCLP_REQ_FAILED) {78 TRACE("sclp request failed\n");79 continue;80 }81 /* if not accepted, retry */82 if (!(sccb->evbuf.hdr.flags & 0x80)) {83 TRACE("sclp request failed: flags=%x\n",84 sccb->evbuf.hdr.flags);85 continue;86 }87 /*88 * for the sync interface the response is in the initial sccb89 */90 if (!sclp_sdias_register.receiver_fn) {91 memcpy(&sdias_evbuf, &sccb->evbuf, sizeof(sdias_evbuf));92 TRACE("sync request done\n");93 return 0;94 }95 /* otherwise we wait for completion */96 wait_for_completion(&evbuf_done);97 TRACE("request done\n");98 return 0;99 }100 return -EIO;101}102 103/*104 * Get number of blocks (4K) available in the HSA105 */106int sclp_sdias_blk_count(void)107{108 struct sdias_sccb *sccb = sclp_sdias_sccb;109 struct sclp_req request;110 int rc;111 112 mutex_lock(&sdias_mutex);113 114 memset(sccb, 0, sizeof(*sccb));115 memset(&request, 0, sizeof(request));116 117 sccb->hdr.length = sizeof(*sccb);118 sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);119 sccb->evbuf.hdr.type = EVTYP_SDIAS;120 sccb->evbuf.event_qual = SDIAS_EQ_SIZE;121 sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;122 sccb->evbuf.event_id = 4712;123 sccb->evbuf.dbs = 1;124 125 request.sccb = sccb;126 request.command = SCLP_CMDW_WRITE_EVENT_DATA;127 request.status = SCLP_REQ_FILLED;128 request.callback = sdias_callback;129 130 rc = sdias_sclp_send(&request);131 if (rc) {132 pr_err("sclp_send failed for get_nr_blocks\n");133 goto out;134 }135 if (sccb->hdr.response_code != 0x0020) {136 TRACE("send failed: %x\n", sccb->hdr.response_code);137 rc = -EIO;138 goto out;139 }140 141 switch (sdias_evbuf.event_status) {142 case 0:143 rc = sdias_evbuf.blk_cnt;144 break;145 default:146 pr_err("SCLP error: %x\n", sdias_evbuf.event_status);147 rc = -EIO;148 goto out;149 }150 TRACE("%i blocks\n", rc);151out:152 mutex_unlock(&sdias_mutex);153 return rc;154}155 156/*157 * Copy from HSA to absolute storage (not reentrant):158 *159 * @dest : Address of buffer where data should be copied160 * @start_blk: Start Block (beginning with 1)161 * @nr_blks : Number of 4K blocks to copy162 *163 * Return Value: 0 : Requested 'number' of blocks of data copied164 * <0: ERROR - negative event status165 */166int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)167{168 struct sdias_sccb *sccb = sclp_sdias_sccb;169 struct sclp_req request;170 int rc;171 172 mutex_lock(&sdias_mutex);173 174 memset(sccb, 0, sizeof(*sccb));175 memset(&request, 0, sizeof(request));176 177 sccb->hdr.length = sizeof(*sccb);178 sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);179 sccb->evbuf.hdr.type = EVTYP_SDIAS;180 sccb->evbuf.hdr.flags = 0;181 sccb->evbuf.event_qual = SDIAS_EQ_STORE_DATA;182 sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;183 sccb->evbuf.event_id = 4712;184 sccb->evbuf.asa_size = SDIAS_ASA_SIZE_64;185 sccb->evbuf.event_status = 0;186 sccb->evbuf.blk_cnt = nr_blks;187 sccb->evbuf.asa = __pa(dest);188 sccb->evbuf.fbn = start_blk;189 sccb->evbuf.lbn = 0;190 sccb->evbuf.dbs = 1;191 192 request.sccb = sccb;193 request.command = SCLP_CMDW_WRITE_EVENT_DATA;194 request.status = SCLP_REQ_FILLED;195 request.callback = sdias_callback;196 197 rc = sdias_sclp_send(&request);198 if (rc) {199 pr_err("sclp_send failed: %x\n", rc);200 goto out;201 }202 if (sccb->hdr.response_code != 0x0020) {203 TRACE("copy failed: %x\n", sccb->hdr.response_code);204 rc = -EIO;205 goto out;206 }207 208 switch (sdias_evbuf.event_status) {209 case SDIAS_EVSTATE_ALL_STORED:210 TRACE("all stored\n");211 break;212 case SDIAS_EVSTATE_PART_STORED:213 TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);214 break;215 case SDIAS_EVSTATE_NO_DATA:216 TRACE("no data\n");217 fallthrough;218 default:219 pr_err("Error from SCLP while copying hsa. Event status = %x\n",220 sdias_evbuf.event_status);221 rc = -EIO;222 }223out:224 mutex_unlock(&sdias_mutex);225 return rc;226}227 228static int __init sclp_sdias_register_check(void)229{230 int rc;231 232 rc = sclp_register(&sclp_sdias_register);233 if (rc)234 return rc;235 if (sclp_sdias_blk_count() == 0) {236 sclp_unregister(&sclp_sdias_register);237 return -ENODEV;238 }239 return 0;240}241 242static int __init sclp_sdias_init_sync(void)243{244 TRACE("Try synchronous mode\n");245 sclp_sdias_register.receive_mask = 0;246 sclp_sdias_register.receiver_fn = NULL;247 return sclp_sdias_register_check();248}249 250static int __init sclp_sdias_init_async(void)251{252 TRACE("Try asynchronous mode\n");253 sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK;254 sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn;255 return sclp_sdias_register_check();256}257 258int __init sclp_sdias_init(void)259{260 if (!is_ipl_type_dump())261 return 0;262 sclp_sdias_sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);263 BUG_ON(!sclp_sdias_sccb);264 sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));265 debug_register_view(sdias_dbf, &debug_sprintf_view);266 debug_set_level(sdias_dbf, 6);267 if (sclp_sdias_init_sync() == 0)268 goto out;269 if (sclp_sdias_init_async() == 0)270 goto out;271 TRACE("init failed\n");272 free_page((unsigned long) sclp_sdias_sccb);273 return -ENODEV;274out:275 TRACE("init done\n");276 return 0;277}278