Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/cio: add eadm subchannel driver

This driver allows usage of EADM subchannels. EADM subchannels
act as a communication vehicle for SCM increments.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Sebastian Ott and committed by
Martin Schwidefsky
eadb86ab 40ff4cc0

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