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