18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCLP "store data in absolute storage" 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2013 68c2ecf20Sopenharmony_ci * Author(s): Michael Holzheu 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_sdias" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/completion.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <asm/sclp.h> 158c2ecf20Sopenharmony_ci#include <asm/debug.h> 168c2ecf20Sopenharmony_ci#include <asm/ipl.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "sclp_sdias.h" 198c2ecf20Sopenharmony_ci#include "sclp.h" 208c2ecf20Sopenharmony_ci#include "sclp_rw.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define SDIAS_RETRIES 300 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct debug_info *sdias_dbf; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct sclp_register sclp_sdias_register = { 298c2ecf20Sopenharmony_ci .send_mask = EVTYP_SDIAS_MASK, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct sdias_sccb *sclp_sdias_sccb; 338c2ecf20Sopenharmony_cistatic struct sdias_evbuf sdias_evbuf; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(evbuf_accepted); 368c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(evbuf_done); 378c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sdias_mutex); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * Called by SCLP base when read event data has been completed (async mode only) 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic void sclp_sdias_receiver_fn(struct evbuf_header *evbuf) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci memcpy(&sdias_evbuf, evbuf, 458c2ecf20Sopenharmony_ci min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length)); 468c2ecf20Sopenharmony_ci complete(&evbuf_done); 478c2ecf20Sopenharmony_ci TRACE("sclp_sdias_receiver_fn done\n"); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Called by SCLP base when sdias event has been accepted 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic void sdias_callback(struct sclp_req *request, void *data) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci complete(&evbuf_accepted); 568c2ecf20Sopenharmony_ci TRACE("callback done\n"); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int sdias_sclp_send(struct sclp_req *req) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct sdias_sccb *sccb = sclp_sdias_sccb; 628c2ecf20Sopenharmony_ci int retries; 638c2ecf20Sopenharmony_ci int rc; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (retries = SDIAS_RETRIES; retries; retries--) { 668c2ecf20Sopenharmony_ci TRACE("add request\n"); 678c2ecf20Sopenharmony_ci rc = sclp_add_request(req); 688c2ecf20Sopenharmony_ci if (rc) { 698c2ecf20Sopenharmony_ci /* not initiated, wait some time and retry */ 708c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 718c2ecf20Sopenharmony_ci TRACE("add request failed: rc = %i\n",rc); 728c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(500)); 738c2ecf20Sopenharmony_ci continue; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci /* initiated, wait for completion of service call */ 768c2ecf20Sopenharmony_ci wait_for_completion(&evbuf_accepted); 778c2ecf20Sopenharmony_ci if (req->status == SCLP_REQ_FAILED) { 788c2ecf20Sopenharmony_ci TRACE("sclp request failed\n"); 798c2ecf20Sopenharmony_ci continue; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci /* if not accepted, retry */ 828c2ecf20Sopenharmony_ci if (!(sccb->evbuf.hdr.flags & 0x80)) { 838c2ecf20Sopenharmony_ci TRACE("sclp request failed: flags=%x\n", 848c2ecf20Sopenharmony_ci sccb->evbuf.hdr.flags); 858c2ecf20Sopenharmony_ci continue; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * for the sync interface the response is in the initial sccb 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (!sclp_sdias_register.receiver_fn) { 918c2ecf20Sopenharmony_ci memcpy(&sdias_evbuf, &sccb->evbuf, sizeof(sdias_evbuf)); 928c2ecf20Sopenharmony_ci TRACE("sync request done\n"); 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci /* otherwise we wait for completion */ 968c2ecf20Sopenharmony_ci wait_for_completion(&evbuf_done); 978c2ecf20Sopenharmony_ci TRACE("request done\n"); 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci return -EIO; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Get number of blocks (4K) available in the HSA 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ciint sclp_sdias_blk_count(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct sdias_sccb *sccb = sclp_sdias_sccb; 1098c2ecf20Sopenharmony_ci struct sclp_req request; 1108c2ecf20Sopenharmony_ci int rc; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci mutex_lock(&sdias_mutex); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci memset(sccb, 0, sizeof(*sccb)); 1158c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci sccb->hdr.length = sizeof(*sccb); 1188c2ecf20Sopenharmony_ci sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); 1198c2ecf20Sopenharmony_ci sccb->evbuf.hdr.type = EVTYP_SDIAS; 1208c2ecf20Sopenharmony_ci sccb->evbuf.event_qual = SDIAS_EQ_SIZE; 1218c2ecf20Sopenharmony_ci sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; 1228c2ecf20Sopenharmony_ci sccb->evbuf.event_id = 4712; 1238c2ecf20Sopenharmony_ci sccb->evbuf.dbs = 1; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci request.sccb = sccb; 1268c2ecf20Sopenharmony_ci request.command = SCLP_CMDW_WRITE_EVENT_DATA; 1278c2ecf20Sopenharmony_ci request.status = SCLP_REQ_FILLED; 1288c2ecf20Sopenharmony_ci request.callback = sdias_callback; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rc = sdias_sclp_send(&request); 1318c2ecf20Sopenharmony_ci if (rc) { 1328c2ecf20Sopenharmony_ci pr_err("sclp_send failed for get_nr_blocks\n"); 1338c2ecf20Sopenharmony_ci goto out; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci if (sccb->hdr.response_code != 0x0020) { 1368c2ecf20Sopenharmony_ci TRACE("send failed: %x\n", sccb->hdr.response_code); 1378c2ecf20Sopenharmony_ci rc = -EIO; 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch (sdias_evbuf.event_status) { 1428c2ecf20Sopenharmony_ci case 0: 1438c2ecf20Sopenharmony_ci rc = sdias_evbuf.blk_cnt; 1448c2ecf20Sopenharmony_ci break; 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci pr_err("SCLP error: %x\n", sdias_evbuf.event_status); 1478c2ecf20Sopenharmony_ci rc = -EIO; 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci TRACE("%i blocks\n", rc); 1518c2ecf20Sopenharmony_ciout: 1528c2ecf20Sopenharmony_ci mutex_unlock(&sdias_mutex); 1538c2ecf20Sopenharmony_ci return rc; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * Copy from HSA to absolute storage (not reentrant): 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * @dest : Address of buffer where data should be copied 1608c2ecf20Sopenharmony_ci * @start_blk: Start Block (beginning with 1) 1618c2ecf20Sopenharmony_ci * @nr_blks : Number of 4K blocks to copy 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * Return Value: 0 : Requested 'number' of blocks of data copied 1648c2ecf20Sopenharmony_ci * <0: ERROR - negative event status 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ciint sclp_sdias_copy(void *dest, int start_blk, int nr_blks) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct sdias_sccb *sccb = sclp_sdias_sccb; 1698c2ecf20Sopenharmony_ci struct sclp_req request; 1708c2ecf20Sopenharmony_ci int rc; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci mutex_lock(&sdias_mutex); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci memset(sccb, 0, sizeof(*sccb)); 1758c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci sccb->hdr.length = sizeof(*sccb); 1788c2ecf20Sopenharmony_ci sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); 1798c2ecf20Sopenharmony_ci sccb->evbuf.hdr.type = EVTYP_SDIAS; 1808c2ecf20Sopenharmony_ci sccb->evbuf.hdr.flags = 0; 1818c2ecf20Sopenharmony_ci sccb->evbuf.event_qual = SDIAS_EQ_STORE_DATA; 1828c2ecf20Sopenharmony_ci sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; 1838c2ecf20Sopenharmony_ci sccb->evbuf.event_id = 4712; 1848c2ecf20Sopenharmony_ci sccb->evbuf.asa_size = SDIAS_ASA_SIZE_64; 1858c2ecf20Sopenharmony_ci sccb->evbuf.event_status = 0; 1868c2ecf20Sopenharmony_ci sccb->evbuf.blk_cnt = nr_blks; 1878c2ecf20Sopenharmony_ci sccb->evbuf.asa = (unsigned long)dest; 1888c2ecf20Sopenharmony_ci sccb->evbuf.fbn = start_blk; 1898c2ecf20Sopenharmony_ci sccb->evbuf.lbn = 0; 1908c2ecf20Sopenharmony_ci sccb->evbuf.dbs = 1; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci request.sccb = sccb; 1938c2ecf20Sopenharmony_ci request.command = SCLP_CMDW_WRITE_EVENT_DATA; 1948c2ecf20Sopenharmony_ci request.status = SCLP_REQ_FILLED; 1958c2ecf20Sopenharmony_ci request.callback = sdias_callback; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rc = sdias_sclp_send(&request); 1988c2ecf20Sopenharmony_ci if (rc) { 1998c2ecf20Sopenharmony_ci pr_err("sclp_send failed: %x\n", rc); 2008c2ecf20Sopenharmony_ci goto out; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci if (sccb->hdr.response_code != 0x0020) { 2038c2ecf20Sopenharmony_ci TRACE("copy failed: %x\n", sccb->hdr.response_code); 2048c2ecf20Sopenharmony_ci rc = -EIO; 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci switch (sdias_evbuf.event_status) { 2098c2ecf20Sopenharmony_ci case SDIAS_EVSTATE_ALL_STORED: 2108c2ecf20Sopenharmony_ci TRACE("all stored\n"); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case SDIAS_EVSTATE_PART_STORED: 2138c2ecf20Sopenharmony_ci TRACE("part stored: %i\n", sdias_evbuf.blk_cnt); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case SDIAS_EVSTATE_NO_DATA: 2168c2ecf20Sopenharmony_ci TRACE("no data\n"); 2178c2ecf20Sopenharmony_ci fallthrough; 2188c2ecf20Sopenharmony_ci default: 2198c2ecf20Sopenharmony_ci pr_err("Error from SCLP while copying hsa. Event status = %x\n", 2208c2ecf20Sopenharmony_ci sdias_evbuf.event_status); 2218c2ecf20Sopenharmony_ci rc = -EIO; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ciout: 2248c2ecf20Sopenharmony_ci mutex_unlock(&sdias_mutex); 2258c2ecf20Sopenharmony_ci return rc; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int __init sclp_sdias_register_check(void) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int rc; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci rc = sclp_register(&sclp_sdias_register); 2338c2ecf20Sopenharmony_ci if (rc) 2348c2ecf20Sopenharmony_ci return rc; 2358c2ecf20Sopenharmony_ci if (sclp_sdias_blk_count() == 0) { 2368c2ecf20Sopenharmony_ci sclp_unregister(&sclp_sdias_register); 2378c2ecf20Sopenharmony_ci return -ENODEV; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int __init sclp_sdias_init_sync(void) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci TRACE("Try synchronous mode\n"); 2458c2ecf20Sopenharmony_ci sclp_sdias_register.receive_mask = 0; 2468c2ecf20Sopenharmony_ci sclp_sdias_register.receiver_fn = NULL; 2478c2ecf20Sopenharmony_ci return sclp_sdias_register_check(); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int __init sclp_sdias_init_async(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci TRACE("Try asynchronous mode\n"); 2538c2ecf20Sopenharmony_ci sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK; 2548c2ecf20Sopenharmony_ci sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn; 2558c2ecf20Sopenharmony_ci return sclp_sdias_register_check(); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciint __init sclp_sdias_init(void) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci if (!is_ipl_type_dump()) 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci sclp_sdias_sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA); 2638c2ecf20Sopenharmony_ci BUG_ON(!sclp_sdias_sccb); 2648c2ecf20Sopenharmony_ci sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); 2658c2ecf20Sopenharmony_ci debug_register_view(sdias_dbf, &debug_sprintf_view); 2668c2ecf20Sopenharmony_ci debug_set_level(sdias_dbf, 6); 2678c2ecf20Sopenharmony_ci if (sclp_sdias_init_sync() == 0) 2688c2ecf20Sopenharmony_ci goto out; 2698c2ecf20Sopenharmony_ci if (sclp_sdias_init_async() == 0) 2708c2ecf20Sopenharmony_ci goto out; 2718c2ecf20Sopenharmony_ci TRACE("init failed\n"); 2728c2ecf20Sopenharmony_ci free_page((unsigned long) sclp_sdias_sccb); 2738c2ecf20Sopenharmony_ci return -ENODEV; 2748c2ecf20Sopenharmony_ciout: 2758c2ecf20Sopenharmony_ci TRACE("init done\n"); 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 278