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

[S390] cio: notify drivers of channel path events

This patch adds a notification mechanism to inform ccw drivers
about changes to channel paths, which occured while the device
is online.

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
585b954e eb4f5d93

+75 -8
+12
arch/s390/include/asm/ccwdev.h
··· 92 92 }; 93 93 94 94 /* 95 + * Possible events used by the path_event notifier. 96 + */ 97 + #define PE_NONE 0x0 98 + #define PE_PATH_GONE 0x1 /* A path is no longer available. */ 99 + #define PE_PATH_AVAILABLE 0x2 /* A path has become available and 100 + was successfully verified. */ 101 + #define PE_PATHGROUP_ESTABLISHED 0x4 /* A pathgroup was reset and had 102 + to be established again. */ 103 + 104 + /* 95 105 * Possible CIO actions triggered by the unit check handler. 96 106 */ 97 107 enum uc_todo { ··· 119 109 * @set_online: called when setting device online 120 110 * @set_offline: called when setting device offline 121 111 * @notify: notify driver of device state changes 112 + * @path_event: notify driver of channel path events 122 113 * @shutdown: called at device shutdown 123 114 * @prepare: prepare for pm state transition 124 115 * @complete: undo work done in @prepare ··· 138 127 int (*set_online) (struct ccw_device *); 139 128 int (*set_offline) (struct ccw_device *); 140 129 int (*notify) (struct ccw_device *, int); 130 + void (*path_event) (struct ccw_device *, int *); 141 131 void (*shutdown) (struct ccw_device *); 142 132 int (*prepare) (struct ccw_device *); 143 133 void (*complete) (struct ccw_device *);
+9
drivers/s390/cio/device.c
··· 1147 1147 static int io_subchannel_chp_event(struct subchannel *sch, 1148 1148 struct chp_link *link, int event) 1149 1149 { 1150 + struct ccw_device *cdev = sch_get_cdev(sch); 1150 1151 int mask; 1151 1152 1152 1153 mask = chp_ssd_get_mask(&sch->ssd_info, link); ··· 1157 1156 case CHP_VARY_OFF: 1158 1157 sch->opm &= ~mask; 1159 1158 sch->lpm &= ~mask; 1159 + if (cdev) 1160 + cdev->private->path_gone_mask |= mask; 1160 1161 io_subchannel_terminate_path(sch, mask); 1161 1162 break; 1162 1163 case CHP_VARY_ON: 1163 1164 sch->opm |= mask; 1164 1165 sch->lpm |= mask; 1166 + if (cdev) 1167 + cdev->private->path_new_mask |= mask; 1165 1168 io_subchannel_verify(sch); 1166 1169 break; 1167 1170 case CHP_OFFLINE: 1168 1171 if (cio_update_schib(sch)) 1169 1172 return -ENODEV; 1173 + if (cdev) 1174 + cdev->private->path_gone_mask |= mask; 1170 1175 io_subchannel_terminate_path(sch, mask); 1171 1176 break; 1172 1177 case CHP_ONLINE: 1173 1178 if (cio_update_schib(sch)) 1174 1179 return -ENODEV; 1175 1180 sch->lpm |= mask & sch->opm; 1181 + if (cdev) 1182 + cdev->private->path_new_mask |= mask; 1176 1183 io_subchannel_verify(sch); 1177 1184 break; 1178 1185 }
+32
drivers/s390/cio/device_fsm.c
··· 349 349 350 350 static void ccw_device_oper_notify(struct ccw_device *cdev) 351 351 { 352 + struct subchannel *sch = to_subchannel(cdev->dev.parent); 353 + 352 354 if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) { 353 355 /* Reenable channel measurements, if needed. */ 354 356 ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); 357 + /* Save indication for new paths. */ 358 + cdev->private->path_new_mask = sch->vpm; 355 359 return; 356 360 } 357 361 /* Driver doesn't want device back. */ ··· 466 462 } 467 463 } 468 464 465 + static void ccw_device_report_path_events(struct ccw_device *cdev) 466 + { 467 + struct subchannel *sch = to_subchannel(cdev->dev.parent); 468 + int path_event[8]; 469 + int chp, mask; 470 + 471 + for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) { 472 + path_event[chp] = PE_NONE; 473 + if (mask & cdev->private->path_gone_mask & ~(sch->vpm)) 474 + path_event[chp] |= PE_PATH_GONE; 475 + if (mask & cdev->private->path_new_mask & sch->vpm) 476 + path_event[chp] |= PE_PATH_AVAILABLE; 477 + if (mask & cdev->private->pgid_reset_mask & sch->vpm) 478 + path_event[chp] |= PE_PATHGROUP_ESTABLISHED; 479 + } 480 + if (cdev->online && cdev->drv->path_event) 481 + cdev->drv->path_event(cdev, path_event); 482 + } 483 + 484 + static void ccw_device_reset_path_events(struct ccw_device *cdev) 485 + { 486 + cdev->private->path_gone_mask = 0; 487 + cdev->private->path_new_mask = 0; 488 + cdev->private->pgid_reset_mask = 0; 489 + } 490 + 469 491 void 470 492 ccw_device_verify_done(struct ccw_device *cdev, int err) 471 493 { ··· 528 498 &cdev->private->irb); 529 499 memset(&cdev->private->irb, 0, sizeof(struct irb)); 530 500 } 501 + ccw_device_report_path_events(cdev); 531 502 break; 532 503 case -ETIME: 533 504 case -EUSERS: ··· 547 516 ccw_device_done(cdev, DEV_STATE_NOT_OPER); 548 517 break; 549 518 } 519 + ccw_device_reset_path_events(cdev); 550 520 } 551 521 552 522 /*
+17 -6
drivers/s390/cio/device_pgid.c
··· 213 213 spid_do(cdev); 214 214 } 215 215 216 + static int pgid_is_reset(struct pgid *p) 217 + { 218 + char *c; 219 + 220 + for (c = (char *)p + 1; c < (char *)(p + 1); c++) { 221 + if (*c != 0) 222 + return 0; 223 + } 224 + return 1; 225 + } 226 + 216 227 static int pgid_cmp(struct pgid *p1, struct pgid *p2) 217 228 { 218 229 return memcmp((char *) p1 + 1, (char *) p2 + 1, ··· 234 223 * Determine pathgroup state from PGID data. 235 224 */ 236 225 static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, 237 - int *mismatch, int *reserved, int *reset) 226 + int *mismatch, int *reserved, u8 *reset) 238 227 { 239 228 struct pgid *pgid = &cdev->private->pgid[0]; 240 229 struct pgid *first = NULL; ··· 249 238 continue; 250 239 if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) 251 240 *reserved = 1; 252 - if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { 253 - /* A PGID was reset. */ 254 - *reset = 1; 241 + if (pgid_is_reset(pgid)) { 242 + *reset |= lpm; 255 243 continue; 256 244 } 257 245 if (!first) { ··· 317 307 struct pgid *pgid; 318 308 int mismatch = 0; 319 309 int reserved = 0; 320 - int reset = 0; 310 + u8 reset = 0; 321 311 u8 donepm; 322 312 323 313 if (rc) ··· 331 321 donepm = pgid_to_donepm(cdev); 332 322 sch->vpm = donepm & sch->opm; 333 323 cdev->private->pgid_todo_mask &= ~donepm; 324 + cdev->private->pgid_reset_mask |= reset; 334 325 pgid_fill(cdev, pgid); 335 326 } 336 327 out: 337 328 CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " 338 - "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid, 329 + "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid, 339 330 id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, 340 331 cdev->private->pgid_todo_mask, mismatch, reserved, reset); 341 332 switch (rc) {
+5 -2
drivers/s390/cio/io_sch.h
··· 151 151 struct subchannel_id schid; /* subchannel number */ 152 152 struct ccw_request req; /* internal I/O request */ 153 153 int iretry; 154 - u8 pgid_valid_mask; /* mask of valid PGIDs */ 155 - u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ 154 + u8 pgid_valid_mask; /* mask of valid PGIDs */ 155 + u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ 156 + u8 pgid_reset_mask; /* mask of PGIDs which were reset */ 157 + u8 path_gone_mask; /* mask of paths, that became unavailable */ 158 + u8 path_new_mask; /* mask of paths, that became available */ 156 159 struct { 157 160 unsigned int fast:1; /* post with "channel end" */ 158 161 unsigned int repall:1; /* report every interrupt status */