linux/drivers/s390/cio/eadm_sch.c
<<
>>
Prefs
   1/*
   2 * Driver for s390 eadm subchannels
   3 *
   4 * Copyright IBM Corp. 2012
   5 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
   6 */
   7
   8#include <linux/kernel_stat.h>
   9#include <linux/completion.h>
  10#include <linux/workqueue.h>
  11#include <linux/spinlock.h>
  12#include <linux/device.h>
  13#include <linux/module.h>
  14#include <linux/timer.h>
  15#include <linux/slab.h>
  16#include <linux/list.h>
  17
  18#include <asm/css_chars.h>
  19#include <asm/debug.h>
  20#include <asm/isc.h>
  21#include <asm/cio.h>
  22#include <asm/scsw.h>
  23#include <asm/eadm.h>
  24
  25#include "eadm_sch.h"
  26#include "ioasm.h"
  27#include "cio.h"
  28#include "css.h"
  29#include "orb.h"
  30
  31MODULE_DESCRIPTION("driver for s390 eadm subchannels");
  32MODULE_LICENSE("GPL");
  33
  34#define EADM_TIMEOUT (7 * HZ)
  35static DEFINE_SPINLOCK(list_lock);
  36static LIST_HEAD(eadm_list);
  37
  38static debug_info_t *eadm_debug;
  39
  40#define EADM_LOG(imp, txt) do {                                 \
  41                debug_text_event(eadm_debug, imp, txt);         \
  42        } while (0)
  43
  44static void EADM_LOG_HEX(int level, void *data, int length)
  45{
  46        if (!debug_level_enabled(eadm_debug, level))
  47                return;
  48        while (length > 0) {
  49                debug_event(eadm_debug, level, data, length);
  50                length -= eadm_debug->buf_size;
  51                data += eadm_debug->buf_size;
  52        }
  53}
  54
  55static void orb_init(union orb *orb)
  56{
  57        memset(orb, 0, sizeof(union orb));
  58        orb->eadm.compat1 = 1;
  59        orb->eadm.compat2 = 1;
  60        orb->eadm.fmt = 1;
  61        orb->eadm.x = 1;
  62}
  63
  64static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob)
  65{
  66        union orb *orb = &get_eadm_private(sch)->orb;
  67        int cc;
  68
  69        orb_init(orb);
  70        orb->eadm.aob = (u32)__pa(aob);
  71        orb->eadm.intparm = (u32)(addr_t)sch;
  72        orb->eadm.key = PAGE_DEFAULT_KEY >> 4;
  73
  74        EADM_LOG(6, "start");
  75        EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid));
  76
  77        cc = ssch(sch->schid, orb);
  78        switch (cc) {
  79        case 0:
  80                sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND;
  81                break;
  82        case 1:         /* status pending */
  83        case 2:         /* busy */
  84                return -EBUSY;
  85        case 3:         /* not operational */
  86                return -ENODEV;
  87        }
  88        return 0;
  89}
  90
  91static int eadm_subchannel_clear(struct subchannel *sch)
  92{
  93        int cc;
  94
  95        cc = csch(sch->schid);
  96        if (cc)
  97                return -ENODEV;
  98
  99        sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND;
 100        return 0;
 101}
 102
 103static void eadm_subchannel_timeout(unsigned long data)
 104{
 105        struct subchannel *sch = (struct subchannel *) data;
 106
 107        spin_lock_irq(sch->lock);
 108        EADM_LOG(1, "timeout");
 109        EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));
 110        if (eadm_subchannel_clear(sch))
 111                EADM_LOG(0, "clear failed");
 112        spin_unlock_irq(sch->lock);
 113}
 114
 115static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
 116{
 117        struct eadm_private *private = get_eadm_private(sch);
 118
 119        if (expires == 0) {
 120                del_timer(&private->timer);
 121                return;
 122        }
 123        if (timer_pending(&private->timer)) {
 124                if (mod_timer(&private->timer, jiffies + expires))
 125                        return;
 126        }
 127        private->timer.function = eadm_subchannel_timeout;
 128        private->timer.data = (unsigned long) sch;
 129        private->timer.expires = jiffies + expires;
 130        add_timer(&private->timer);
 131}
 132
 133static void eadm_subchannel_irq(struct subchannel *sch)
 134{
 135        struct eadm_private *private = get_eadm_private(sch);
 136        struct eadm_scsw *scsw = &sch->schib.scsw.eadm;
 137        struct irb *irb = this_cpu_ptr(&cio_irb);
 138        int error = 0;
 139
 140        EADM_LOG(6, "irq");
 141        EADM_LOG_HEX(6, irb, sizeof(*irb));
 142
 143        inc_irq_stat(IRQIO_ADM);
 144
 145        if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))
 146            && scsw->eswf == 1 && irb->esw.eadm.erw.r)
 147                error = -EIO;
 148
 149        if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC)
 150                error = -ETIMEDOUT;
 151
 152        eadm_subchannel_set_timeout(sch, 0);
 153
 154        if (private->state != EADM_BUSY) {
 155                EADM_LOG(1, "irq unsol");
 156                EADM_LOG_HEX(1, irb, sizeof(*irb));
 157                private->state = EADM_NOT_OPER;
 158                css_sched_sch_todo(sch, SCH_TODO_EVAL);
 159                return;
 160        }
 161        scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error);
 162        private->state = EADM_IDLE;
 163
 164        if (private->completion)
 165                complete(private->completion);
 166}
 167
 168static struct subchannel *eadm_get_idle_sch(void)
 169{
 170        struct eadm_private *private;
 171        struct subchannel *sch;
 172        unsigned long flags;
 173
 174        spin_lock_irqsave(&list_lock, flags);
 175        list_for_each_entry(private, &eadm_list, head) {
 176                sch = private->sch;
 177                spin_lock(sch->lock);
 178                if (private->state == EADM_IDLE) {
 179                        private->state = EADM_BUSY;
 180                        list_move_tail(&private->head, &eadm_list);
 181                        spin_unlock(sch->lock);
 182                        spin_unlock_irqrestore(&list_lock, flags);
 183
 184                        return sch;
 185                }
 186                spin_unlock(sch->lock);
 187        }
 188        spin_unlock_irqrestore(&list_lock, flags);
 189
 190        return NULL;
 191}
 192
 193int eadm_start_aob(struct aob *aob)
 194{
 195        struct eadm_private *private;
 196        struct subchannel *sch;
 197        unsigned long flags;
 198        int ret;
 199
 200        sch = eadm_get_idle_sch();
 201        if (!sch)
 202                return -EBUSY;
 203
 204        spin_lock_irqsave(sch->lock, flags);
 205        eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);
 206        ret = eadm_subchannel_start(sch, aob);
 207        if (!ret)
 208                goto out_unlock;
 209
 210        /* Handle start subchannel failure. */
 211        eadm_subchannel_set_timeout(sch, 0);
 212        private = get_eadm_private(sch);
 213        private->state = EADM_NOT_OPER;
 214        css_sched_sch_todo(sch, SCH_TODO_EVAL);
 215
 216out_unlock:
 217        spin_unlock_irqrestore(sch->lock, flags);
 218
 219        return ret;
 220}
 221EXPORT_SYMBOL_GPL(eadm_start_aob);
 222
 223static int eadm_subchannel_probe(struct subchannel *sch)
 224{
 225        struct eadm_private *private;
 226        int ret;
 227
 228        private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
 229        if (!private)
 230                return -ENOMEM;
 231
 232        INIT_LIST_HEAD(&private->head);
 233        init_timer(&private->timer);
 234
 235        spin_lock_irq(sch->lock);
 236        set_eadm_private(sch, private);
 237        private->state = EADM_IDLE;
 238        private->sch = sch;
 239        sch->isc = EADM_SCH_ISC;
 240        ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
 241        if (ret) {
 242                set_eadm_private(sch, NULL);
 243                spin_unlock_irq(sch->lock);
 244                kfree(private);
 245                goto out;
 246        }
 247        spin_unlock_irq(sch->lock);
 248
 249        spin_lock_irq(&list_lock);
 250        list_add(&private->head, &eadm_list);
 251        spin_unlock_irq(&list_lock);
 252
 253        if (dev_get_uevent_suppress(&sch->dev)) {
 254                dev_set_uevent_suppress(&sch->dev, 0);
 255                kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
 256        }
 257out:
 258        return ret;
 259}
 260
 261static void eadm_quiesce(struct subchannel *sch)
 262{
 263        struct eadm_private *private = get_eadm_private(sch);
 264        DECLARE_COMPLETION_ONSTACK(completion);
 265        int ret;
 266
 267        spin_lock_irq(sch->lock);
 268        if (private->state != EADM_BUSY)
 269                goto disable;
 270
 271        if (eadm_subchannel_clear(sch))
 272                goto disable;
 273
 274        private->completion = &completion;
 275        spin_unlock_irq(sch->lock);
 276
 277        wait_for_completion_io(&completion);
 278
 279        spin_lock_irq(sch->lock);
 280        private->completion = NULL;
 281
 282disable:
 283        eadm_subchannel_set_timeout(sch, 0);
 284        do {
 285                ret = cio_disable_subchannel(sch);
 286        } while (ret == -EBUSY);
 287
 288        spin_unlock_irq(sch->lock);
 289}
 290
 291static int eadm_subchannel_remove(struct subchannel *sch)
 292{
 293        struct eadm_private *private = get_eadm_private(sch);
 294
 295        spin_lock_irq(&list_lock);
 296        list_del(&private->head);
 297        spin_unlock_irq(&list_lock);
 298
 299        eadm_quiesce(sch);
 300
 301        spin_lock_irq(sch->lock);
 302        set_eadm_private(sch, NULL);
 303        spin_unlock_irq(sch->lock);
 304
 305        kfree(private);
 306
 307        return 0;
 308}
 309
 310static void eadm_subchannel_shutdown(struct subchannel *sch)
 311{
 312        eadm_quiesce(sch);
 313}
 314
 315static int eadm_subchannel_freeze(struct subchannel *sch)
 316{
 317        return cio_disable_subchannel(sch);
 318}
 319
 320static int eadm_subchannel_restore(struct subchannel *sch)
 321{
 322        return cio_enable_subchannel(sch, (u32)(unsigned long)sch);
 323}
 324
 325/**
 326 * eadm_subchannel_sch_event - process subchannel event
 327 * @sch: subchannel
 328 * @process: non-zero if function is called in process context
 329 *
 330 * An unspecified event occurred for this subchannel. Adjust data according
 331 * to the current operational state of the subchannel. Return zero when the
 332 * event has been handled sufficiently or -EAGAIN when this function should
 333 * be called again in process context.
 334 */
 335static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
 336{
 337        struct eadm_private *private;
 338        unsigned long flags;
 339        int ret = 0;
 340
 341        spin_lock_irqsave(sch->lock, flags);
 342        if (!device_is_registered(&sch->dev))
 343                goto out_unlock;
 344
 345        if (work_pending(&sch->todo_work))
 346                goto out_unlock;
 347
 348        if (cio_update_schib(sch)) {
 349                css_sched_sch_todo(sch, SCH_TODO_UNREG);
 350                goto out_unlock;
 351        }
 352        private = get_eadm_private(sch);
 353        if (private->state == EADM_NOT_OPER)
 354                private->state = EADM_IDLE;
 355
 356out_unlock:
 357        spin_unlock_irqrestore(sch->lock, flags);
 358
 359        return ret;
 360}
 361
 362static struct css_device_id eadm_subchannel_ids[] = {
 363        { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, },
 364        { /* end of list */ },
 365};
 366MODULE_DEVICE_TABLE(css, eadm_subchannel_ids);
 367
 368static struct css_driver eadm_subchannel_driver = {
 369        .drv = {
 370                .name = "eadm_subchannel",
 371                .owner = THIS_MODULE,
 372        },
 373        .subchannel_type = eadm_subchannel_ids,
 374        .irq = eadm_subchannel_irq,
 375        .probe = eadm_subchannel_probe,
 376        .remove = eadm_subchannel_remove,
 377        .shutdown = eadm_subchannel_shutdown,
 378        .sch_event = eadm_subchannel_sch_event,
 379        .freeze = eadm_subchannel_freeze,
 380        .thaw = eadm_subchannel_restore,
 381        .restore = eadm_subchannel_restore,
 382};
 383
 384static int __init eadm_sch_init(void)
 385{
 386        int ret;
 387
 388        if (!css_general_characteristics.eadm)
 389                return -ENXIO;
 390
 391        eadm_debug = debug_register("eadm_log", 16, 1, 16);
 392        if (!eadm_debug)
 393                return -ENOMEM;
 394
 395        debug_register_view(eadm_debug, &debug_hex_ascii_view);
 396        debug_set_level(eadm_debug, 2);
 397
 398        isc_register(EADM_SCH_ISC);
 399        ret = css_driver_register(&eadm_subchannel_driver);
 400        if (ret)
 401                goto cleanup;
 402
 403        return ret;
 404
 405cleanup:
 406        isc_unregister(EADM_SCH_ISC);
 407        debug_unregister(eadm_debug);
 408        return ret;
 409}
 410
 411static void __exit eadm_sch_exit(void)
 412{
 413        css_driver_unregister(&eadm_subchannel_driver);
 414        isc_unregister(EADM_SCH_ISC);
 415        debug_unregister(eadm_debug);
 416}
 417module_init(eadm_sch_init);
 418module_exit(eadm_sch_exit);
 419