cio_inject.c (3736B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * CIO inject interface 4 * 5 * Copyright IBM Corp. 2021 6 * Author(s): Vineeth Vijayan <vneethv@linux.ibm.com> 7 */ 8 9#define KMSG_COMPONENT "cio" 10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11 12#include <linux/slab.h> 13#include <linux/spinlock.h> 14#include <linux/mm.h> 15#include <linux/debugfs.h> 16#include <asm/chpid.h> 17 18#include "cio_inject.h" 19#include "cio_debug.h" 20 21static DEFINE_SPINLOCK(crw_inject_lock); 22DEFINE_STATIC_KEY_FALSE(cio_inject_enabled); 23static struct crw *crw_inject_data; 24 25/** 26 * crw_inject : Initiate the artificial CRW inject 27 * @crw: The data which needs to be injected as new CRW. 28 * 29 * The CRW handler is called, which will use the provided artificial 30 * data instead of the CRW from the underlying hardware. 31 * 32 * Return: 0 on success 33 */ 34static int crw_inject(struct crw *crw) 35{ 36 int rc = 0; 37 struct crw *copy; 38 unsigned long flags; 39 40 copy = kmemdup(crw, sizeof(*crw), GFP_KERNEL); 41 if (!copy) 42 return -ENOMEM; 43 44 spin_lock_irqsave(&crw_inject_lock, flags); 45 if (crw_inject_data) { 46 kfree(copy); 47 rc = -EBUSY; 48 } else { 49 crw_inject_data = copy; 50 } 51 spin_unlock_irqrestore(&crw_inject_lock, flags); 52 53 if (!rc) 54 crw_handle_channel_report(); 55 56 return rc; 57} 58 59/** 60 * stcrw_get_injected: Copy the artificial CRW data to CRW struct. 61 * @crw: The target CRW pointer. 62 * 63 * Retrieve an injected CRW data. Return 0 on success, 1 if no 64 * injected-CRW is available. The function reproduces the return 65 * code of the actual STCRW function. 66 */ 67int stcrw_get_injected(struct crw *crw) 68{ 69 int rc = 1; 70 unsigned long flags; 71 72 spin_lock_irqsave(&crw_inject_lock, flags); 73 if (crw_inject_data) { 74 memcpy(crw, crw_inject_data, sizeof(*crw)); 75 kfree(crw_inject_data); 76 crw_inject_data = NULL; 77 rc = 0; 78 } 79 spin_unlock_irqrestore(&crw_inject_lock, flags); 80 81 return rc; 82} 83 84/* The debugfs write handler for crw_inject nodes operation */ 85static ssize_t crw_inject_write(struct file *file, const char __user *buf, 86 size_t lbuf, loff_t *ppos) 87{ 88 u32 slct, oflw, chn, rsc, anc, erc, rsid; 89 struct crw crw; 90 char *buffer; 91 int rc; 92 93 if (!static_branch_likely(&cio_inject_enabled)) { 94 pr_warn("CIO inject is not enabled - ignoring CRW inject\n"); 95 return -EINVAL; 96 } 97 98 buffer = vmemdup_user(buf, lbuf); 99 if (IS_ERR(buffer)) 100 return -ENOMEM; 101 102 rc = sscanf(buffer, "%x %x %x %x %x %x %x", &slct, &oflw, &chn, &rsc, &anc, 103 &erc, &rsid); 104 105 kvfree(buffer); 106 if (rc != 7) { 107 pr_warn("crw_inject: Invalid format (need <solicited> <overflow> <chaining> <rsc> <ancillary> <erc> <rsid>)\n"); 108 return -EINVAL; 109 } 110 111 memset(&crw, 0, sizeof(crw)); 112 crw.slct = slct; 113 crw.oflw = oflw; 114 crw.chn = chn; 115 crw.rsc = rsc; 116 crw.anc = anc; 117 crw.erc = erc; 118 crw.rsid = rsid; 119 120 rc = crw_inject(&crw); 121 if (rc) 122 return rc; 123 124 return lbuf; 125} 126 127/* Debugfs write handler for inject_enable node*/ 128static ssize_t enable_inject_write(struct file *file, const char __user *buf, 129 size_t lbuf, loff_t *ppos) 130{ 131 unsigned long en = 0; 132 int rc; 133 134 rc = kstrtoul_from_user(buf, lbuf, 10, &en); 135 if (rc) 136 return rc; 137 138 switch (en) { 139 case 0: 140 static_branch_disable(&cio_inject_enabled); 141 break; 142 case 1: 143 static_branch_enable(&cio_inject_enabled); 144 break; 145 } 146 147 return lbuf; 148} 149 150static const struct file_operations crw_fops = { 151 .owner = THIS_MODULE, 152 .write = crw_inject_write, 153}; 154 155static const struct file_operations cio_en_fops = { 156 .owner = THIS_MODULE, 157 .write = enable_inject_write, 158}; 159 160static int __init cio_inject_init(void) 161{ 162 /* enable_inject node enables the static branching */ 163 debugfs_create_file("enable_inject", 0200, cio_debugfs_dir, 164 NULL, &cio_en_fops); 165 166 debugfs_create_file("crw_inject", 0200, cio_debugfs_dir, 167 NULL, &crw_fops); 168 return 0; 169} 170 171device_initcall(cio_inject_init);