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

s390: add scm notification

Detect an scm change notification in store event information.
Update affected scm devices and notify their drivers.

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
40ff4cc0 1d1c8f78

+74 -1
+1
arch/s390/include/asm/eadm.h
··· 102 102 struct device_driver drv; 103 103 int (*probe) (struct scm_device *scmdev); 104 104 int (*remove) (struct scm_device *scmdev); 105 + void (*notify) (struct scm_device *scmdev); 105 106 void (*handler) (struct scm_device *scmdev, void *data, int error); 106 107 }; 107 108
+17
drivers/s390/cio/chsc.c
··· 398 398 } 399 399 } 400 400 401 + static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area) 402 + { 403 + int ret; 404 + 405 + CIO_CRW_EVENT(4, "chsc: scm change notification\n"); 406 + if (sei_area->rs != 7) 407 + return; 408 + 409 + ret = scm_update_information(); 410 + if (ret) 411 + CIO_CRW_EVENT(0, "chsc: updating change notification" 412 + " failed (rc=%d).\n", ret); 413 + } 414 + 401 415 static void chsc_process_sei(struct chsc_sei_area *sei_area) 402 416 { 403 417 /* Check if we might have lost some information. */ ··· 432 418 break; 433 419 case 8: /* channel-path-configuration notification */ 434 420 chsc_process_sei_chp_config(sei_area); 421 + break; 422 + case 12: /* scm change notification */ 423 + chsc_process_sei_scm_change(sei_area); 435 424 break; 436 425 default: /* other stuff */ 437 426 CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
+7
drivers/s390/cio/chsc.h
··· 154 154 155 155 int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token); 156 156 157 + #ifdef CONFIG_SCM_BUS 158 + int scm_update_information(void); 159 + #else /* CONFIG_SCM_BUS */ 160 + #define scm_update_information() 0 161 + #endif /* CONFIG_SCM_BUS */ 162 + 163 + 157 164 #endif
+49 -1
drivers/s390/cio/scm.c
··· 196 196 spin_lock_init(&scmdev->lock); 197 197 } 198 198 199 + /* 200 + * Check for state-changes, notify the driver and userspace. 201 + */ 202 + static void scmdev_update(struct scm_device *scmdev, struct sale *sale) 203 + { 204 + struct scm_driver *scmdrv; 205 + bool changed; 206 + 207 + device_lock(&scmdev->dev); 208 + changed = scmdev->attrs.rank != sale->rank || 209 + scmdev->attrs.oper_state != sale->op_state; 210 + scmdev->attrs.rank = sale->rank; 211 + scmdev->attrs.oper_state = sale->op_state; 212 + if (!scmdev->dev.driver) 213 + goto out; 214 + scmdrv = to_scm_drv(scmdev->dev.driver); 215 + if (changed && scmdrv->notify) 216 + scmdrv->notify(scmdev); 217 + out: 218 + device_unlock(&scmdev->dev); 219 + if (changed) 220 + kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE); 221 + } 222 + 223 + static int check_address(struct device *dev, void *data) 224 + { 225 + struct scm_device *scmdev = to_scm_dev(dev); 226 + struct sale *sale = data; 227 + 228 + return scmdev->address == sale->sa; 229 + } 230 + 231 + static struct scm_device *scmdev_find(struct sale *sale) 232 + { 233 + struct device *dev; 234 + 235 + dev = bus_find_device(&scm_bus_type, NULL, sale, check_address); 236 + 237 + return dev ? to_scm_dev(dev) : NULL; 238 + } 239 + 199 240 static int scm_add(struct chsc_scm_info *scm_info, size_t num) 200 241 { 201 242 struct sale *sale, *scmal = scm_info->scmal; ··· 244 203 int ret; 245 204 246 205 for (sale = scmal; sale < scmal + num; sale++) { 206 + scmdev = scmdev_find(sale); 207 + if (scmdev) { 208 + scmdev_update(scmdev, sale); 209 + /* Release reference from scm_find(). */ 210 + put_device(&scmdev->dev); 211 + continue; 212 + } 247 213 scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL); 248 214 if (!scmdev) 249 215 return -ENODEV; ··· 266 218 return 0; 267 219 } 268 220 269 - static int scm_update_information(void) 221 + int scm_update_information(void) 270 222 { 271 223 struct chsc_scm_info *scm_info; 272 224 u64 token = 0;