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

s390: add scm bus driver

Bus driver to manage Storage Class Memory.

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
1d1c8f78 184b08af

+330
+7
arch/s390/Kconfig
··· 433 433 434 434 If unsure, say N. 435 435 436 + config SCM_BUS 437 + def_bool y 438 + depends on 64BIT 439 + prompt "SCM bus driver" 440 + help 441 + Bus driver for Storage Class Memory. 442 + 436 443 endmenu 437 444 438 445 menu "Dump support"
+51
arch/s390/include/asm/eadm.h
··· 2 2 #define _ASM_S390_EADM_H 3 3 4 4 #include <linux/types.h> 5 + #include <linux/device.h> 6 + #include <linux/spinlock.h> 5 7 6 8 struct arqb { 7 9 u64 data; ··· 72 70 struct arsb response; 73 71 struct msb msb[AOB_NR_MSB]; 74 72 } __packed __aligned(PAGE_SIZE); 73 + 74 + struct aob_rq_header { 75 + struct scm_device *scmdev; 76 + char data[0]; 77 + }; 78 + 79 + struct scm_device { 80 + u64 address; 81 + u64 size; 82 + unsigned int nr_max_block; 83 + struct device dev; 84 + spinlock_t lock; 85 + struct { 86 + unsigned int persistence:4; 87 + unsigned int oper_state:4; 88 + unsigned int data_state:4; 89 + unsigned int rank:4; 90 + unsigned int release:1; 91 + unsigned int res_id:8; 92 + } __packed attrs; 93 + }; 94 + 95 + #define OP_STATE_GOOD 1 96 + #define OP_STATE_TEMP_ERR 2 97 + #define OP_STATE_PERM_ERR 3 98 + 99 + struct scm_driver { 100 + struct device_driver drv; 101 + int (*probe) (struct scm_device *scmdev); 102 + int (*remove) (struct scm_device *scmdev); 103 + void (*handler) (struct scm_device *scmdev, void *data, int error); 104 + }; 105 + 106 + int scm_driver_register(struct scm_driver *scmdrv); 107 + void scm_driver_unregister(struct scm_driver *scmdrv); 108 + 109 + int scm_start_aob(struct aob *aob); 110 + void scm_irq_handler(struct aob *aob, int error); 111 + 112 + struct eadm_ops { 113 + int (*eadm_start) (struct aob *aob); 114 + struct module *owner; 115 + }; 116 + 117 + int scm_get_ref(void); 118 + void scm_put_ref(void); 119 + 120 + void register_eadm_ops(struct eadm_ops *ops); 121 + void unregister_eadm_ops(struct eadm_ops *ops); 75 122 76 123 #endif /* _ASM_S390_EADM_H */
+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_SCM_BUS) += scm.o 11 12 obj-$(CONFIG_CCWGROUP) += ccwgroup.o 12 13 13 14 qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
+271
drivers/s390/cio/scm.c
··· 1 + /* 2 + * Recognize and maintain s390 storage class memory. 3 + * 4 + * Copyright IBM Corp. 2012 5 + * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com> 6 + */ 7 + 8 + #include <linux/spinlock.h> 9 + #include <linux/device.h> 10 + #include <linux/module.h> 11 + #include <linux/mutex.h> 12 + #include <linux/slab.h> 13 + #include <linux/init.h> 14 + #include <linux/err.h> 15 + #include <asm/eadm.h> 16 + #include "chsc.h" 17 + 18 + static struct device *scm_root; 19 + static struct eadm_ops *eadm_ops; 20 + static DEFINE_MUTEX(eadm_ops_mutex); 21 + 22 + #define to_scm_dev(n) container_of(n, struct scm_device, dev) 23 + #define to_scm_drv(d) container_of(d, struct scm_driver, drv) 24 + 25 + static int scmdev_probe(struct device *dev) 26 + { 27 + struct scm_device *scmdev = to_scm_dev(dev); 28 + struct scm_driver *scmdrv = to_scm_drv(dev->driver); 29 + 30 + return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV; 31 + } 32 + 33 + static int scmdev_remove(struct device *dev) 34 + { 35 + struct scm_device *scmdev = to_scm_dev(dev); 36 + struct scm_driver *scmdrv = to_scm_drv(dev->driver); 37 + 38 + return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV; 39 + } 40 + 41 + static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env) 42 + { 43 + return add_uevent_var(env, "MODALIAS=scm:scmdev"); 44 + } 45 + 46 + static struct bus_type scm_bus_type = { 47 + .name = "scm", 48 + .probe = scmdev_probe, 49 + .remove = scmdev_remove, 50 + .uevent = scmdev_uevent, 51 + }; 52 + 53 + /** 54 + * scm_driver_register() - register a scm driver 55 + * @scmdrv: driver to be registered 56 + */ 57 + int scm_driver_register(struct scm_driver *scmdrv) 58 + { 59 + struct device_driver *drv = &scmdrv->drv; 60 + 61 + drv->bus = &scm_bus_type; 62 + 63 + return driver_register(drv); 64 + } 65 + EXPORT_SYMBOL_GPL(scm_driver_register); 66 + 67 + /** 68 + * scm_driver_unregister() - deregister a scm driver 69 + * @scmdrv: driver to be deregistered 70 + */ 71 + void scm_driver_unregister(struct scm_driver *scmdrv) 72 + { 73 + driver_unregister(&scmdrv->drv); 74 + } 75 + EXPORT_SYMBOL_GPL(scm_driver_unregister); 76 + 77 + int scm_get_ref(void) 78 + { 79 + int ret = 0; 80 + 81 + mutex_lock(&eadm_ops_mutex); 82 + if (!eadm_ops || !try_module_get(eadm_ops->owner)) 83 + ret = -ENOENT; 84 + mutex_unlock(&eadm_ops_mutex); 85 + 86 + return ret; 87 + } 88 + EXPORT_SYMBOL_GPL(scm_get_ref); 89 + 90 + void scm_put_ref(void) 91 + { 92 + mutex_lock(&eadm_ops_mutex); 93 + module_put(eadm_ops->owner); 94 + mutex_unlock(&eadm_ops_mutex); 95 + } 96 + EXPORT_SYMBOL_GPL(scm_put_ref); 97 + 98 + void register_eadm_ops(struct eadm_ops *ops) 99 + { 100 + mutex_lock(&eadm_ops_mutex); 101 + eadm_ops = ops; 102 + mutex_unlock(&eadm_ops_mutex); 103 + } 104 + EXPORT_SYMBOL_GPL(register_eadm_ops); 105 + 106 + void unregister_eadm_ops(struct eadm_ops *ops) 107 + { 108 + mutex_lock(&eadm_ops_mutex); 109 + eadm_ops = NULL; 110 + mutex_unlock(&eadm_ops_mutex); 111 + } 112 + EXPORT_SYMBOL_GPL(unregister_eadm_ops); 113 + 114 + int scm_start_aob(struct aob *aob) 115 + { 116 + return eadm_ops->eadm_start(aob); 117 + } 118 + EXPORT_SYMBOL_GPL(scm_start_aob); 119 + 120 + void scm_irq_handler(struct aob *aob, int error) 121 + { 122 + struct aob_rq_header *aobrq = (void *) aob->request.data; 123 + struct scm_device *scmdev = aobrq->scmdev; 124 + struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver); 125 + 126 + scmdrv->handler(scmdev, aobrq->data, error); 127 + } 128 + EXPORT_SYMBOL_GPL(scm_irq_handler); 129 + 130 + #define scm_attr(name) \ 131 + static ssize_t show_##name(struct device *dev, \ 132 + struct device_attribute *attr, char *buf) \ 133 + { \ 134 + struct scm_device *scmdev = to_scm_dev(dev); \ 135 + int ret; \ 136 + \ 137 + spin_lock(&scmdev->lock); \ 138 + ret = sprintf(buf, "%u\n", scmdev->attrs.name); \ 139 + spin_unlock(&scmdev->lock); \ 140 + \ 141 + return ret; \ 142 + } \ 143 + static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); 144 + 145 + scm_attr(persistence); 146 + scm_attr(oper_state); 147 + scm_attr(data_state); 148 + scm_attr(rank); 149 + scm_attr(release); 150 + scm_attr(res_id); 151 + 152 + static struct attribute *scmdev_attrs[] = { 153 + &dev_attr_persistence.attr, 154 + &dev_attr_oper_state.attr, 155 + &dev_attr_data_state.attr, 156 + &dev_attr_rank.attr, 157 + &dev_attr_release.attr, 158 + &dev_attr_res_id.attr, 159 + NULL, 160 + }; 161 + 162 + static struct attribute_group scmdev_attr_group = { 163 + .attrs = scmdev_attrs, 164 + }; 165 + 166 + static const struct attribute_group *scmdev_attr_groups[] = { 167 + &scmdev_attr_group, 168 + NULL, 169 + }; 170 + 171 + static void scmdev_release(struct device *dev) 172 + { 173 + struct scm_device *scmdev = to_scm_dev(dev); 174 + 175 + kfree(scmdev); 176 + } 177 + 178 + static void scmdev_setup(struct scm_device *scmdev, struct sale *sale, 179 + unsigned int size, unsigned int max_blk_count) 180 + { 181 + dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa); 182 + scmdev->nr_max_block = max_blk_count; 183 + scmdev->address = sale->sa; 184 + scmdev->size = 1UL << size; 185 + scmdev->attrs.rank = sale->rank; 186 + scmdev->attrs.persistence = sale->p; 187 + scmdev->attrs.oper_state = sale->op_state; 188 + scmdev->attrs.data_state = sale->data_state; 189 + scmdev->attrs.rank = sale->rank; 190 + scmdev->attrs.release = sale->r; 191 + scmdev->attrs.res_id = sale->rid; 192 + scmdev->dev.parent = scm_root; 193 + scmdev->dev.bus = &scm_bus_type; 194 + scmdev->dev.release = scmdev_release; 195 + scmdev->dev.groups = scmdev_attr_groups; 196 + spin_lock_init(&scmdev->lock); 197 + } 198 + 199 + static int scm_add(struct chsc_scm_info *scm_info, size_t num) 200 + { 201 + struct sale *sale, *scmal = scm_info->scmal; 202 + struct scm_device *scmdev; 203 + int ret; 204 + 205 + for (sale = scmal; sale < scmal + num; sale++) { 206 + scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL); 207 + if (!scmdev) 208 + return -ENODEV; 209 + scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc); 210 + ret = device_register(&scmdev->dev); 211 + if (ret) { 212 + /* Release reference from device_initialize(). */ 213 + put_device(&scmdev->dev); 214 + return ret; 215 + } 216 + } 217 + 218 + return 0; 219 + } 220 + 221 + static int scm_update_information(void) 222 + { 223 + struct chsc_scm_info *scm_info; 224 + u64 token = 0; 225 + size_t num; 226 + int ret; 227 + 228 + scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); 229 + if (!scm_info) 230 + return -ENOMEM; 231 + 232 + do { 233 + ret = chsc_scm_info(scm_info, token); 234 + if (ret) 235 + break; 236 + 237 + num = (scm_info->response.length - 238 + (offsetof(struct chsc_scm_info, scmal) - 239 + offsetof(struct chsc_scm_info, response)) 240 + ) / sizeof(struct sale); 241 + 242 + ret = scm_add(scm_info, num); 243 + if (ret) 244 + break; 245 + 246 + token = scm_info->restok; 247 + } while (token); 248 + 249 + free_page((unsigned long)scm_info); 250 + 251 + return ret; 252 + } 253 + 254 + static int __init scm_init(void) 255 + { 256 + int ret; 257 + 258 + ret = bus_register(&scm_bus_type); 259 + if (ret) 260 + return ret; 261 + 262 + scm_root = root_device_register("scm"); 263 + if (IS_ERR(scm_root)) { 264 + bus_unregister(&scm_bus_type); 265 + return PTR_ERR(scm_root); 266 + } 267 + 268 + scm_update_information(); 269 + return 0; 270 + } 271 + subsys_initcall_sync(scm_init);