Linux Audio

Check our new training course

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