sclp_sdias.c (6735B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * SCLP "store data in absolute storage" 4 * 5 * Copyright IBM Corp. 2003, 2013 6 * Author(s): Michael Holzheu 7 */ 8 9#define KMSG_COMPONENT "sclp_sdias" 10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 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 300 25 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 accepted 52 */ 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 sccb 89 */ 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 HSA 105 */ 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 copied 160 * @start_blk: Start Block (beginning with 1) 161 * @nr_blks : Number of 4K blocks to copy 162 * 163 * Return Value: 0 : Requested 'number' of blocks of data copied 164 * <0: ERROR - negative event status 165 */ 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}