File Index
Files and directories
Sign in
FILE

scm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/s390/cio/scm.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Recognize and maintain s390 storage class memory.
  4 *
  5 * Copyright IBM Corp. 2012
  6 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
  7 */
  8
  9#include <linux/device.h>
 10#include <linux/export.h>
 11#include <linux/module.h>
 12#include <linux/mutex.h>
 13#include <linux/slab.h>
 14#include <linux/init.h>
 15#include <linux/err.h>
 16#include <asm/eadm.h>
 17#include "chsc.h"
 18
 19static struct device *scm_root;
 20
 21#define to_scm_dev(n) container_of(n, struct scm_device, dev)
 22#define	to_scm_drv(d) container_of(d, struct scm_driver, drv)
 23
 24static int scmdev_probe(struct device *dev)
 25{
 26	struct scm_device *scmdev = to_scm_dev(dev);
 27	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
 28
 29	return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
 30}
 31
 32static void scmdev_remove(struct device *dev)
 33{
 34	struct scm_device *scmdev = to_scm_dev(dev);
 35	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
 36
 37	if (scmdrv->remove)
 38		scmdrv->remove(scmdev);
 39}
 40
 41static int scmdev_uevent(const struct device *dev, struct kobj_uevent_env *env)
 42{
 43	return add_uevent_var(env, "MODALIAS=scm:scmdev");
 44}
 45
 46static const 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 */
 57int 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}
 65EXPORT_SYMBOL_GPL(scm_driver_register);
 66
 67/**
 68 * scm_driver_unregister() - deregister a scm driver
 69 * @scmdrv: driver to be deregistered
 70 */
 71void scm_driver_unregister(struct scm_driver *scmdrv)
 72{
 73	driver_unregister(&scmdrv->drv);
 74}
 75EXPORT_SYMBOL_GPL(scm_driver_unregister);
 76
 77void scm_irq_handler(struct aob *aob, blk_status_t error)
 78{
 79	struct aob_rq_header *aobrq = (void *) aob->request.data;
 80	struct scm_device *scmdev = aobrq->scmdev;
 81	struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
 82
 83	scmdrv->handler(scmdev, aobrq->data, error);
 84}
 85EXPORT_SYMBOL_GPL(scm_irq_handler);
 86
 87#define scm_attr(name)							\
 88static ssize_t show_##name(struct device *dev,				\
 89	       struct device_attribute *attr, char *buf)		\
 90{									\
 91	struct scm_device *scmdev = to_scm_dev(dev);			\
 92	int ret;							\
 93									\
 94	device_lock(dev);						\
 95	ret = sysfs_emit(buf, "%u\n", scmdev->attrs.name);		\
 96	device_unlock(dev);						\
 97									\
 98	return ret;							\
 99}									\
100static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
101
102scm_attr(persistence);
103scm_attr(oper_state);
104scm_attr(data_state);
105scm_attr(rank);
106scm_attr(release);
107scm_attr(res_id);
108
109static struct attribute *scmdev_attrs[] = {
110	&dev_attr_persistence.attr,
111	&dev_attr_oper_state.attr,
112	&dev_attr_data_state.attr,
113	&dev_attr_rank.attr,
114	&dev_attr_release.attr,
115	&dev_attr_res_id.attr,
116	NULL,
117};
118
119static struct attribute_group scmdev_attr_group = {
120	.attrs = scmdev_attrs,
121};
122
123static const struct attribute_group *scmdev_attr_groups[] = {
124	&scmdev_attr_group,
125	NULL,
126};
127
128static void scmdev_release(struct device *dev)
129{
130	struct scm_device *scmdev = to_scm_dev(dev);
131
132	kfree(scmdev);
133}
134
135static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
136			 unsigned int size, unsigned int max_blk_count)
137{
138	dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
139	scmdev->nr_max_block = max_blk_count;
140	scmdev->address = sale->sa;
141	scmdev->size = 1UL << size;
142	scmdev->attrs.rank = sale->rank;
143	scmdev->attrs.persistence = sale->p;
144	scmdev->attrs.oper_state = sale->op_state;
145	scmdev->attrs.data_state = sale->data_state;
146	scmdev->attrs.rank = sale->rank;
147	scmdev->attrs.release = sale->r;
148	scmdev->attrs.res_id = sale->rid;
149	scmdev->dev.parent = scm_root;
150	scmdev->dev.bus = &scm_bus_type;
151	scmdev->dev.release = scmdev_release;
152	scmdev->dev.groups = scmdev_attr_groups;
153}
154
155/*
156 * Check for state-changes, notify the driver and userspace.
157 */
158static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
159{
160	struct scm_driver *scmdrv;
161	bool changed;
162
163	device_lock(&scmdev->dev);
164	changed = scmdev->attrs.rank != sale->rank ||
165		  scmdev->attrs.oper_state != sale->op_state;
166	scmdev->attrs.rank = sale->rank;
167	scmdev->attrs.oper_state = sale->op_state;
168	if (!scmdev->dev.driver)
169		goto out;
170	scmdrv = to_scm_drv(scmdev->dev.driver);
171	if (changed && scmdrv->notify)
172		scmdrv->notify(scmdev, SCM_CHANGE);
173out:
174	device_unlock(&scmdev->dev);
175	if (changed)
176		kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
177}
178
179static int check_address(struct device *dev, const void *data)
180{
181	struct scm_device *scmdev = to_scm_dev(dev);
182	const struct sale *sale = data;
183
184	return scmdev->address == sale->sa;
185}
186
187static struct scm_device *scmdev_find(struct sale *sale)
188{
189	struct device *dev;
190
191	dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
192
193	return dev ? to_scm_dev(dev) : NULL;
194}
195
196static int scm_add(struct chsc_scm_info *scm_info, size_t num)
197{
198	struct sale *sale, *scmal = scm_info->scmal;
199	struct scm_device *scmdev;
200	int ret;
201
202	for (sale = scmal; sale < scmal + num; sale++) {
203		scmdev = scmdev_find(sale);
204		if (scmdev) {
205			scmdev_update(scmdev, sale);
206			/* Release reference from scm_find(). */
207			put_device(&scmdev->dev);
208			continue;
209		}
210		scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
211		if (!scmdev)
212			return -ENODEV;
213		scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
214		ret = device_register(&scmdev->dev);
215		if (ret) {
216			/* Release reference from device_initialize(). */
217			put_device(&scmdev->dev);
218			return ret;
219		}
220	}
221
222	return 0;
223}
224
225int scm_update_information(void)
226{
227	struct chsc_scm_info *scm_info;
228	u64 token = 0;
229	size_t num;
230	int ret;
231
232	scm_info = (void *)__get_free_page(GFP_KERNEL);
233	if (!scm_info)
234		return -ENOMEM;
235
236	do {
237		ret = chsc_scm_info(scm_info, token);
238		if (ret)
239			break;
240
241		num = (scm_info->response.length -
242		       (offsetof(struct chsc_scm_info, scmal) -
243			offsetof(struct chsc_scm_info, response))
244		      ) / sizeof(struct sale);
245
246		ret = scm_add(scm_info, num);
247		if (ret)
248			break;
249
250		token = scm_info->restok;
251	} while (token);
252
253	free_page((unsigned long)scm_info);
254
255	return ret;
256}
257
258static int scm_dev_avail(struct device *dev, void *unused)
259{
260	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
261	struct scm_device *scmdev = to_scm_dev(dev);
262
263	if (dev->driver && scmdrv->notify)
264		scmdrv->notify(scmdev, SCM_AVAIL);
265
266	return 0;
267}
268
269int scm_process_availability_information(void)
270{
271	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail);
272}
273
274static int __init scm_init(void)
275{
276	int ret;
277
278	ret = bus_register(&scm_bus_type);
279	if (ret)
280		return ret;
281
282	scm_root = root_device_register("scm");
283	if (IS_ERR(scm_root)) {
284		bus_unregister(&scm_bus_type);
285		return PTR_ERR(scm_root);
286	}
287
288	scm_update_information();
289	return 0;
290}
291subsys_initcall_sync(scm_init);
~/ Read-only file preview.