File Index
Files and directories
Sign in
FILE

blacklist.c

File preview with syntax highlighting.

linux-6.19.14/drivers/s390/cio/blacklist.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *   S/390 common I/O routines -- blacklisting of specific devices
  4 *
  5 *    Copyright IBM Corp. 1999, 2013
  6 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  7 *		 Cornelia Huck (cornelia.huck@de.ibm.com)
  8 *		 Arnd Bergmann (arndb@de.ibm.com)
  9 */
 10
 11#define pr_fmt(fmt) "cio: " fmt
 12
 13#include <linux/init.h>
 14#include <linux/vmalloc.h>
 15#include <linux/proc_fs.h>
 16#include <linux/seq_file.h>
 17#include <linux/ctype.h>
 18#include <linux/device.h>
 19
 20#include <linux/uaccess.h>
 21#include <asm/cio.h>
 22#include <asm/ipl.h>
 23
 24#include "blacklist.h"
 25#include "cio.h"
 26#include "cio_debug.h"
 27#include "css.h"
 28#include "device.h"
 29
 30/*
 31 * "Blacklisting" of certain devices:
 32 * Device numbers given in the commandline as cio_ignore=... won't be known
 33 * to Linux.
 34 *
 35 * These can be single devices or ranges of devices
 36 */
 37
 38/* 65536 bits for each set to indicate if a devno is blacklisted or not */
 39#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
 40			 (8*sizeof(long)))
 41static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
 42typedef enum {add, free} range_action;
 43
 44/*
 45 * Function: blacklist_range
 46 * (Un-)blacklist the devices from-to
 47 */
 48static int blacklist_range(range_action action, unsigned int from_ssid,
 49			   unsigned int to_ssid, unsigned int from,
 50			   unsigned int to, int msgtrigger)
 51{
 52	if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
 53		if (msgtrigger)
 54			pr_warn("0.%x.%04x to 0.%x.%04x is not a valid range for cio_ignore\n",
 55				from_ssid, from, to_ssid, to);
 56
 57		return 1;
 58	}
 59
 60	while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
 61	       (from <= to))) {
 62		if (action == add)
 63			set_bit(from, bl_dev[from_ssid]);
 64		else
 65			clear_bit(from, bl_dev[from_ssid]);
 66		from++;
 67		if (from > __MAX_SUBCHANNEL) {
 68			from_ssid++;
 69			from = 0;
 70		}
 71	}
 72
 73	return 0;
 74}
 75
 76static int pure_hex(char **cp, unsigned int *val, int min_digit,
 77		    int max_digit, int max_val)
 78{
 79	int diff;
 80
 81	diff = 0;
 82	*val = 0;
 83
 84	while (diff <= max_digit) {
 85		int value = hex_to_bin(**cp);
 86
 87		if (value < 0)
 88			break;
 89		*val = *val * 16 + value;
 90		(*cp)++;
 91		diff++;
 92	}
 93
 94	if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
 95		return 1;
 96
 97	return 0;
 98}
 99
100static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
101		       unsigned int *devno, int msgtrigger)
102{
103	char *str_work;
104	int val, rc, ret;
105
106	rc = 1;
107
108	if (*str == '\0')
109		goto out;
110
111	/* old style */
112	str_work = str;
113	val = simple_strtoul(str, &str_work, 16);
114
115	if (*str_work == '\0') {
116		if (val <= __MAX_SUBCHANNEL) {
117			*devno = val;
118			*ssid = 0;
119			*cssid = 0;
120			rc = 0;
121		}
122		goto out;
123	}
124
125	/* new style */
126	str_work = str;
127	ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
128	if (ret || (str_work[0] != '.'))
129		goto out;
130	str_work++;
131	ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
132	if (ret || (str_work[0] != '.'))
133		goto out;
134	str_work++;
135	ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
136	if (ret || (str_work[0] != '\0'))
137		goto out;
138
139	rc = 0;
140out:
141	if (rc && msgtrigger)
142		pr_warn("%s is not a valid device for the cio_ignore kernel parameter\n",
143			str);
144
145	return rc;
146}
147
148static int blacklist_parse_parameters(char *str, range_action action,
149				      int msgtrigger)
150{
151	unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
152	int rc, totalrc;
153	char *parm;
154	range_action ra;
155
156	totalrc = 0;
157
158	while ((parm = strsep(&str, ","))) {
159		rc = 0;
160		ra = action;
161		if (*parm == '!') {
162			if (ra == add)
163				ra = free;
164			else
165				ra = add;
166			parm++;
167		}
168		if (strcmp(parm, "all") == 0) {
169			from_cssid = 0;
170			from_ssid = 0;
171			from = 0;
172			to_cssid = __MAX_CSSID;
173			to_ssid = __MAX_SSID;
174			to = __MAX_SUBCHANNEL;
175		} else if (strcmp(parm, "ipldev") == 0) {
176			if (ipl_info.type == IPL_TYPE_CCW) {
177				from_cssid = 0;
178				from_ssid = ipl_info.data.ccw.dev_id.ssid;
179				from = ipl_info.data.ccw.dev_id.devno;
180			} else if (ipl_info.type == IPL_TYPE_FCP ||
181				   ipl_info.type == IPL_TYPE_FCP_DUMP) {
182				from_cssid = 0;
183				from_ssid = ipl_info.data.fcp.dev_id.ssid;
184				from = ipl_info.data.fcp.dev_id.devno;
185			} else {
186				continue;
187			}
188			to_cssid = from_cssid;
189			to_ssid = from_ssid;
190			to = from;
191		} else if (strcmp(parm, "condev") == 0) {
192			if (console_devno == -1)
193				continue;
194
195			from_cssid = to_cssid = 0;
196			from_ssid = to_ssid = 0;
197			from = to = console_devno;
198		} else {
199			rc = parse_busid(strsep(&parm, "-"), &from_cssid,
200					 &from_ssid, &from, msgtrigger);
201			if (!rc) {
202				if (parm != NULL)
203					rc = parse_busid(parm, &to_cssid,
204							 &to_ssid, &to,
205							 msgtrigger);
206				else {
207					to_cssid = from_cssid;
208					to_ssid = from_ssid;
209					to = from;
210				}
211			}
212		}
213		if (!rc) {
214			rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
215					     msgtrigger);
216			if (rc)
217				totalrc = -EINVAL;
218		} else
219			totalrc = -EINVAL;
220	}
221
222	return totalrc;
223}
224
225static int __init
226blacklist_setup (char *str)
227{
228	CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
229	if (blacklist_parse_parameters(str, add, 1))
230		return 0;
231	return 1;
232}
233
234__setup ("cio_ignore=", blacklist_setup);
235
236/* Checking if devices are blacklisted */
237
238/*
239 * Function: is_blacklisted
240 * Returns 1 if the given devicenumber can be found in the blacklist,
241 * otherwise 0.
242 * Used by validate_subchannel()
243 */
244int
245is_blacklisted (int ssid, int devno)
246{
247	return test_bit (devno, bl_dev[ssid]);
248}
249
250#ifdef CONFIG_PROC_FS
251/*
252 * Function: blacklist_parse_proc_parameters
253 * parse the stuff which is piped to /proc/cio_ignore
254 */
255static int blacklist_parse_proc_parameters(char *buf)
256{
257	int rc;
258	char *parm;
259
260	parm = strsep(&buf, " ");
261
262	if (strcmp("free", parm) == 0) {
263		rc = blacklist_parse_parameters(buf, free, 0);
264		/*
265		 * Evaluate the subchannels without an online device. This way,
266		 * no path-verification will be triggered on those subchannels
267		 * and it avoids unnecessary delays.
268		 */
269		css_schedule_eval_cond(CSS_EVAL_NOT_ONLINE, 0);
270	} else if (strcmp("add", parm) == 0)
271		rc = blacklist_parse_parameters(buf, add, 0);
272	else if (strcmp("purge", parm) == 0)
273		return ccw_purge_blacklisted();
274	else
275		return -EINVAL;
276
277
278	return rc;
279}
280
281/* Iterator struct for all devices. */
282struct ccwdev_iter {
283	int devno;
284	int ssid;
285	int in_range;
286};
287
288static void *
289cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
290{
291	struct ccwdev_iter *iter = s->private;
292
293	if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
294		return NULL;
295	memset(iter, 0, sizeof(*iter));
296	iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
297	iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
298	return iter;
299}
300
301static void
302cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
303{
304}
305
306static void *
307cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
308{
309	struct ccwdev_iter *iter;
310	loff_t p = *offset;
311
312	(*offset)++;
313	if (p >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
314		return NULL;
315	iter = it;
316	if (iter->devno == __MAX_SUBCHANNEL) {
317		iter->devno = 0;
318		iter->ssid++;
319		if (iter->ssid > __MAX_SSID)
320			return NULL;
321	} else
322		iter->devno++;
323	return iter;
324}
325
326static int
327cio_ignore_proc_seq_show(struct seq_file *s, void *it)
328{
329	struct ccwdev_iter *iter;
330
331	iter = it;
332	if (!is_blacklisted(iter->ssid, iter->devno))
333		/* Not blacklisted, nothing to output. */
334		return 0;
335	if (!iter->in_range) {
336		/* First device in range. */
337		if ((iter->devno == __MAX_SUBCHANNEL) ||
338		    !is_blacklisted(iter->ssid, iter->devno + 1)) {
339			/* Singular device. */
340			seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
341			return 0;
342		}
343		iter->in_range = 1;
344		seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
345		return 0;
346	}
347	if ((iter->devno == __MAX_SUBCHANNEL) ||
348	    !is_blacklisted(iter->ssid, iter->devno + 1)) {
349		/* Last device in range. */
350		iter->in_range = 0;
351		seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
352	}
353	return 0;
354}
355
356static ssize_t
357cio_ignore_write(struct file *file, const char __user *user_buf,
358		 size_t user_len, loff_t *offset)
359{
360	char *buf;
361	ssize_t rc, ret, i;
362
363	if (*offset)
364		return -EINVAL;
365	if (user_len > 65536)
366		user_len = 65536;
367	buf = vzalloc(user_len + 1); /* maybe better use the stack? */
368	if (buf == NULL)
369		return -ENOMEM;
370
371	if (strncpy_from_user (buf, user_buf, user_len) < 0) {
372		rc = -EFAULT;
373		goto out_free;
374	}
375
376	i = user_len - 1;
377	while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
378		buf[i] = '\0';
379		i--;
380	}
381	ret = blacklist_parse_proc_parameters(buf);
382	if (ret)
383		rc = ret;
384	else
385		rc = user_len;
386
387out_free:
388	vfree (buf);
389	return rc;
390}
391
392static const struct seq_operations cio_ignore_proc_seq_ops = {
393	.start = cio_ignore_proc_seq_start,
394	.stop  = cio_ignore_proc_seq_stop,
395	.next  = cio_ignore_proc_seq_next,
396	.show  = cio_ignore_proc_seq_show,
397};
398
399static int
400cio_ignore_proc_open(struct inode *inode, struct file *file)
401{
402	return seq_open_private(file, &cio_ignore_proc_seq_ops,
403				sizeof(struct ccwdev_iter));
404}
405
406static const struct proc_ops cio_ignore_proc_ops = {
407	.proc_open	= cio_ignore_proc_open,
408	.proc_read	= seq_read,
409	.proc_lseek	= seq_lseek,
410	.proc_release	= seq_release_private,
411	.proc_write	= cio_ignore_write,
412};
413
414static int
415cio_ignore_proc_init (void)
416{
417	struct proc_dir_entry *entry;
418
419	entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL,
420			    &cio_ignore_proc_ops);
421	if (!entry)
422		return -ENOENT;
423	return 0;
424}
425
426__initcall (cio_ignore_proc_init);
427
428#endif /* CONFIG_PROC_FS */
~/ Read-only file preview.