• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/s390/cio/
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	unsigned int value;
83
84	diff = 0;
85	*val = 0;
86
87	while (isxdigit(**cp) && (diff <= max_digit)) {
88
89		if (isdigit(**cp))
90			value = **cp - '0';
91		else
92			value = tolower(**cp) - 'a' + 10;
93		*val = *val * 16 + value;
94		(*cp)++;
95		diff++;
96	}
97
98	if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
99		return 1;
100
101	return 0;
102}
103
104static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
105		       unsigned int *devno, int msgtrigger)
106{
107	char *str_work;
108	int val, rc, ret;
109
110	rc = 1;
111
112	if (*str == '\0')
113		goto out;
114
115	/* old style */
116	str_work = str;
117	val = simple_strtoul(str, &str_work, 16);
118
119	if (*str_work == '\0') {
120		if (val <= __MAX_SUBCHANNEL) {
121			*devno = val;
122			*ssid = 0;
123			*cssid = 0;
124			rc = 0;
125		}
126		goto out;
127	}
128
129	/* new style */
130	str_work = str;
131	ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
132	if (ret || (str_work[0] != '.'))
133		goto out;
134	str_work++;
135	ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
136	if (ret || (str_work[0] != '.'))
137		goto out;
138	str_work++;
139	ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
140	if (ret || (str_work[0] != '\0'))
141		goto out;
142
143	rc = 0;
144out:
145	if (rc && msgtrigger)
146		pr_warning("%s is not a valid device for the cio_ignore "
147			   "kernel parameter\n", str);
148
149	return rc;
150}
151
152static int blacklist_parse_parameters(char *str, range_action action,
153				      int msgtrigger)
154{
155	unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
156	int rc, totalrc;
157	char *parm;
158	range_action ra;
159
160	totalrc = 0;
161
162	while ((parm = strsep(&str, ","))) {
163		rc = 0;
164		ra = action;
165		if (*parm == '!') {
166			if (ra == add)
167				ra = free;
168			else
169				ra = add;
170			parm++;
171		}
172		if (strcmp(parm, "all") == 0) {
173			from_cssid = 0;
174			from_ssid = 0;
175			from = 0;
176			to_cssid = __MAX_CSSID;
177			to_ssid = __MAX_SSID;
178			to = __MAX_SUBCHANNEL;
179		} else {
180			rc = parse_busid(strsep(&parm, "-"), &from_cssid,
181					 &from_ssid, &from, msgtrigger);
182			if (!rc) {
183				if (parm != NULL)
184					rc = parse_busid(parm, &to_cssid,
185							 &to_ssid, &to,
186							 msgtrigger);
187				else {
188					to_cssid = from_cssid;
189					to_ssid = from_ssid;
190					to = from;
191				}
192			}
193		}
194		if (!rc) {
195			rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
196					     msgtrigger);
197			if (rc)
198				totalrc = -EINVAL;
199		} else
200			totalrc = -EINVAL;
201	}
202
203	return totalrc;
204}
205
206static int __init
207blacklist_setup (char *str)
208{
209	CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
210	if (blacklist_parse_parameters(str, add, 1))
211		return 0;
212	return 1;
213}
214
215__setup ("cio_ignore=", blacklist_setup);
216
217/* Checking if devices are blacklisted */
218
219/*
220 * Function: is_blacklisted
221 * Returns 1 if the given devicenumber can be found in the blacklist,
222 * otherwise 0.
223 * Used by validate_subchannel()
224 */
225int
226is_blacklisted (int ssid, int devno)
227{
228	return test_bit (devno, bl_dev[ssid]);
229}
230
231#ifdef CONFIG_PROC_FS
232/*
233 * Function: blacklist_parse_proc_parameters
234 * parse the stuff which is piped to /proc/cio_ignore
235 */
236static int blacklist_parse_proc_parameters(char *buf)
237{
238	int rc;
239	char *parm;
240
241	parm = strsep(&buf, " ");
242
243	if (strcmp("free", parm) == 0)
244		rc = blacklist_parse_parameters(buf, free, 0);
245	else if (strcmp("add", parm) == 0)
246		rc = blacklist_parse_parameters(buf, add, 0);
247	else if (strcmp("purge", parm) == 0)
248		return ccw_purge_blacklisted();
249	else
250		return -EINVAL;
251
252	css_schedule_reprobe();
253
254	return rc;
255}
256
257/* Iterator struct for all devices. */
258struct ccwdev_iter {
259	int devno;
260	int ssid;
261	int in_range;
262};
263
264static void *
265cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
266{
267	struct ccwdev_iter *iter = s->private;
268
269	if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
270		return NULL;
271	memset(iter, 0, sizeof(*iter));
272	iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
273	iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
274	return iter;
275}
276
277static void
278cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
279{
280}
281
282static void *
283cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
284{
285	struct ccwdev_iter *iter;
286
287	if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
288		return NULL;
289	iter = it;
290	if (iter->devno == __MAX_SUBCHANNEL) {
291		iter->devno = 0;
292		iter->ssid++;
293		if (iter->ssid > __MAX_SSID)
294			return NULL;
295	} else
296		iter->devno++;
297	(*offset)++;
298	return iter;
299}
300
301static int
302cio_ignore_proc_seq_show(struct seq_file *s, void *it)
303{
304	struct ccwdev_iter *iter;
305
306	iter = it;
307	if (!is_blacklisted(iter->ssid, iter->devno))
308		/* Not blacklisted, nothing to output. */
309		return 0;
310	if (!iter->in_range) {
311		/* First device in range. */
312		if ((iter->devno == __MAX_SUBCHANNEL) ||
313		    !is_blacklisted(iter->ssid, iter->devno + 1))
314			/* Singular device. */
315			return seq_printf(s, "0.%x.%04x\n",
316					  iter->ssid, iter->devno);
317		iter->in_range = 1;
318		return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
319	}
320	if ((iter->devno == __MAX_SUBCHANNEL) ||
321	    !is_blacklisted(iter->ssid, iter->devno + 1)) {
322		/* Last device in range. */
323		iter->in_range = 0;
324		return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
325	}
326	return 0;
327}
328
329static ssize_t
330cio_ignore_write(struct file *file, const char __user *user_buf,
331		 size_t user_len, loff_t *offset)
332{
333	char *buf;
334	ssize_t rc, ret, i;
335
336	if (*offset)
337		return -EINVAL;
338	if (user_len > 65536)
339		user_len = 65536;
340	buf = vmalloc (user_len + 1); /* maybe better use the stack? */
341	if (buf == NULL)
342		return -ENOMEM;
343	memset(buf, 0, user_len + 1);
344
345	if (strncpy_from_user (buf, user_buf, user_len) < 0) {
346		rc = -EFAULT;
347		goto out_free;
348	}
349
350	i = user_len - 1;
351	while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
352		buf[i] = '\0';
353		i--;
354	}
355	ret = blacklist_parse_proc_parameters(buf);
356	if (ret)
357		rc = ret;
358	else
359		rc = user_len;
360
361out_free:
362	vfree (buf);
363	return rc;
364}
365
366static const struct seq_operations cio_ignore_proc_seq_ops = {
367	.start = cio_ignore_proc_seq_start,
368	.stop  = cio_ignore_proc_seq_stop,
369	.next  = cio_ignore_proc_seq_next,
370	.show  = cio_ignore_proc_seq_show,
371};
372
373static int
374cio_ignore_proc_open(struct inode *inode, struct file *file)
375{
376	return seq_open_private(file, &cio_ignore_proc_seq_ops,
377				sizeof(struct ccwdev_iter));
378}
379
380static const struct file_operations cio_ignore_proc_fops = {
381	.open    = cio_ignore_proc_open,
382	.read    = seq_read,
383	.llseek  = seq_lseek,
384	.release = seq_release_private,
385	.write   = cio_ignore_write,
386};
387
388static int
389cio_ignore_proc_init (void)
390{
391	struct proc_dir_entry *entry;
392
393	entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL,
394			    &cio_ignore_proc_fops);
395	if (!entry)
396		return -ENOENT;
397	return 0;
398}
399
400__initcall (cio_ignore_proc_init);
401
402#endif /* CONFIG_PROC_FS */
403