1495 lines · 37.7 KB · C
RawDownload
		return -EINVAL;	}	v3pr_info("op='%c'\n", op); 	for (; *str ; ++str) {		for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {			if (*str == opt_array[i].opt_char) {				modifiers->flags |= opt_array[i].flag;				break;			}		}		if (i < 0) {			pr_err("unknown flag '%c'\n", *str);			return -EINVAL;		}	}	v3pr_info("flags=0x%x\n", modifiers->flags); 	/* calculate final flags, mask based upon op */	switch (op) {	case '=':		/* modifiers->flags already set */		modifiers->mask = 0;		break;	case '+':		modifiers->mask = ~0U;		break;	case '-':		modifiers->mask = ~modifiers->flags;		modifiers->flags = 0;		break;	}	v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); 	return 0;} static int ddebug_exec_query(char *query_string, const char *modname){	struct flag_settings modifiers = {};	struct ddebug_query query = {};#define MAXWORDS 9	int nwords, nfound;	char *words[MAXWORDS]; 	nwords = ddebug_tokenize(query_string, words, MAXWORDS);	if (nwords <= 0) {		pr_err("tokenize failed\n");		return -EINVAL;	}	/* check flags 1st (last arg) so query is pairs of spec,val */	if (ddebug_parse_flags(words[nwords-1], &modifiers)) {		pr_err("flags parse failed\n");		return -EINVAL;	}	if (ddebug_parse_query(words, nwords-1, &query, modname)) {		pr_err("query parse failed\n");		return -EINVAL;	}	/* actually go and implement the change */	nfound = ddebug_change(&query, &modifiers);	vpr_info_dq(&query, nfound ? "applied" : "no-match"); 	return nfound;} /* handle multiple queries in query string, continue on error, return   last error or number of matching callsites.  Module name is either   in param (for boot arg) or perhaps in query string.*/static int ddebug_exec_queries(char *query, const char *modname){	char *split;	int i, errs = 0, exitcode = 0, rc, nfound = 0; 	for (i = 0; query; query = split) {		split = strpbrk(query, ";\n");		if (split)			*split++ = '\0'; 		query = skip_spaces(query);		if (!query || !*query || *query == '#')			continue; 		vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*"); 		rc = ddebug_exec_query(query, modname);		if (rc < 0) {			errs++;			exitcode = rc;		} else {			nfound += rc;		}		i++;	}	if (i)		v2pr_info("processed %d queries, with %d matches, %d errs\n",			 i, nfound, errs); 	if (exitcode)		return exitcode;	return nfound;} /* apply a new bitmap to the sys-knob's current bit-state */static int ddebug_apply_class_bitmap(const struct ddebug_class_param *dcp,				     unsigned long *new_bits, unsigned long *old_bits){#define QUERY_SIZE 128	char query[QUERY_SIZE];	const struct ddebug_class_map *map = dcp->map;	int matches = 0;	int bi, ct; 	v2pr_info("apply: 0x%lx to: 0x%lx\n", *new_bits, *old_bits); 	for (bi = 0; bi < map->length; bi++) {		if (test_bit(bi, new_bits) == test_bit(bi, old_bits))			continue; 		snprintf(query, QUERY_SIZE, "class %s %c%s", map->class_names[bi],			 test_bit(bi, new_bits) ? '+' : '-', dcp->flags); 		ct = ddebug_exec_queries(query, NULL);		matches += ct; 		v2pr_info("bit_%d: %d matches on class: %s -> 0x%lx\n", bi,			  ct, map->class_names[bi], *new_bits);	}	return matches;} /* stub to later conditionally add "$module." prefix where not already done */#define KP_NAME(kp)	kp->name #define CLASSMAP_BITMASK(width) ((1UL << (width)) - 1) /* accept comma-separated-list of [+-] classnames */static int param_set_dyndbg_classnames(const char *instr, const struct kernel_param *kp){	const struct ddebug_class_param *dcp = kp->arg;	const struct ddebug_class_map *map = dcp->map;	unsigned long curr_bits, old_bits;	char *cl_str, *p, *tmp;	int cls_id, totct = 0;	bool wanted; 	cl_str = tmp = kstrdup_and_replace(instr, '\n', '\0', GFP_KERNEL);	if (!tmp)		return -ENOMEM; 	/* start with previously set state-bits, then modify */	curr_bits = old_bits = *dcp->bits;	vpr_info("\"%s\" > %s:0x%lx\n", cl_str, KP_NAME(kp), curr_bits); 	for (; cl_str; cl_str = p) {		p = strchr(cl_str, ',');		if (p)			*p++ = '\0'; 		if (*cl_str == '-') {			wanted = false;			cl_str++;		} else {			wanted = true;			if (*cl_str == '+')				cl_str++;		}		cls_id = match_string(map->class_names, map->length, cl_str);		if (cls_id < 0) {			pr_err("%s unknown to %s\n", cl_str, KP_NAME(kp));			continue;		} 		/* have one or more valid class_ids of one *_NAMES type */		switch (map->map_type) {		case DD_CLASS_TYPE_DISJOINT_NAMES:			/* the +/- pertains to a single bit */			if (test_bit(cls_id, &curr_bits) == wanted) {				v3pr_info("no change on %s\n", cl_str);				continue;			}			curr_bits ^= BIT(cls_id);			totct += ddebug_apply_class_bitmap(dcp, &curr_bits, dcp->bits);			*dcp->bits = curr_bits;			v2pr_info("%s: changed bit %d:%s\n", KP_NAME(kp), cls_id,				  map->class_names[cls_id]);			break;		case DD_CLASS_TYPE_LEVEL_NAMES:			/* cls_id = N in 0..max. wanted +/- determines N or N-1 */			old_bits = CLASSMAP_BITMASK(*dcp->lvl);			curr_bits = CLASSMAP_BITMASK(cls_id + (wanted ? 1 : 0 )); 			totct += ddebug_apply_class_bitmap(dcp, &curr_bits, &old_bits);			*dcp->lvl = (cls_id + (wanted ? 1 : 0));			v2pr_info("%s: changed bit-%d: \"%s\" %lx->%lx\n", KP_NAME(kp), cls_id,				  map->class_names[cls_id], old_bits, curr_bits);			break;		default:			pr_err("illegal map-type value %d\n", map->map_type);		}	}	kfree(tmp);	vpr_info("total matches: %d\n", totct);	return 0;} /** * param_set_dyndbg_classes - class FOO >control * @instr: string echo>d to sysfs, input depends on map_type * @kp:    kp->arg has state: bits/lvl, map, map_type * * Enable/disable prdbgs by their class, as given in the arguments to * DECLARE_DYNDBG_CLASSMAP.  For LEVEL map-types, enforce relative * levels by bitpos. * * Returns: 0 or <0 if error. */int param_set_dyndbg_classes(const char *instr, const struct kernel_param *kp){	const struct ddebug_class_param *dcp = kp->arg;	const struct ddebug_class_map *map = dcp->map;	unsigned long inrep, new_bits, old_bits;	int rc, totct = 0; 	switch (map->map_type) { 	case DD_CLASS_TYPE_DISJOINT_NAMES:	case DD_CLASS_TYPE_LEVEL_NAMES:		/* handle [+-]classnames list separately, we are done here */		return param_set_dyndbg_classnames(instr, kp); 	case DD_CLASS_TYPE_DISJOINT_BITS:	case DD_CLASS_TYPE_LEVEL_NUM:		/* numeric input, accept and fall-thru */		rc = kstrtoul(instr, 0, &inrep);		if (rc) {			pr_err("expecting numeric input: %s > %s\n", instr, KP_NAME(kp));			return -EINVAL;		}		break;	default:		pr_err("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);		return -EINVAL;	} 	/* only _BITS,_NUM (numeric) map-types get here */	switch (map->map_type) {	case DD_CLASS_TYPE_DISJOINT_BITS:		/* expect bits. mask and warn if too many */		if (inrep & ~CLASSMAP_BITMASK(map->length)) {			pr_warn("%s: input: 0x%lx exceeds mask: 0x%lx, masking\n",				KP_NAME(kp), inrep, CLASSMAP_BITMASK(map->length));			inrep &= CLASSMAP_BITMASK(map->length);		}		v2pr_info("bits:%lx > %s\n", inrep, KP_NAME(kp));		totct += ddebug_apply_class_bitmap(dcp, &inrep, dcp->bits);		*dcp->bits = inrep;		break;	case DD_CLASS_TYPE_LEVEL_NUM:		/* input is bitpos, of highest verbosity to be enabled */		if (inrep > map->length) {			pr_warn("%s: level:%ld exceeds max:%d, clamping\n",				KP_NAME(kp), inrep, map->length);			inrep = map->length;		}		old_bits = CLASSMAP_BITMASK(*dcp->lvl);		new_bits = CLASSMAP_BITMASK(inrep);		v2pr_info("lvl:%ld bits:0x%lx > %s\n", inrep, new_bits, KP_NAME(kp));		totct += ddebug_apply_class_bitmap(dcp, &new_bits, &old_bits);		*dcp->lvl = inrep;		break;	default:		pr_warn("%s: bad map type: %d\n", KP_NAME(kp), map->map_type);	}	vpr_info("%s: total matches: %d\n", KP_NAME(kp), totct);	return 0;}EXPORT_SYMBOL(param_set_dyndbg_classes); /** * param_get_dyndbg_classes - classes reader * @buffer: string description of controlled bits -> classes * @kp:     kp->arg has state: bits, map * * Reads last written state, underlying prdbg state may have been * altered by direct >control.  Displays 0x for DISJOINT, 0-N for * LEVEL Returns: #chars written or <0 on error */int param_get_dyndbg_classes(char *buffer, const struct kernel_param *kp){	const struct ddebug_class_param *dcp = kp->arg;	const struct ddebug_class_map *map = dcp->map; 	switch (map->map_type) { 	case DD_CLASS_TYPE_DISJOINT_NAMES:	case DD_CLASS_TYPE_DISJOINT_BITS:		return scnprintf(buffer, PAGE_SIZE, "0x%lx\n", *dcp->bits); 	case DD_CLASS_TYPE_LEVEL_NAMES:	case DD_CLASS_TYPE_LEVEL_NUM:		return scnprintf(buffer, PAGE_SIZE, "%d\n", *dcp->lvl);	default:		return -1;	}}EXPORT_SYMBOL(param_get_dyndbg_classes); const struct kernel_param_ops param_ops_dyndbg_classes = {	.set = param_set_dyndbg_classes,	.get = param_get_dyndbg_classes,};EXPORT_SYMBOL(param_ops_dyndbg_classes); #define PREFIX_SIZE 128 static int remaining(int wrote){	if (PREFIX_SIZE - wrote > 0)		return PREFIX_SIZE - wrote;	return 0;} static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf){	int pos_after_tid;	int pos = 0; 	if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {		if (in_interrupt())			pos += snprintf(buf + pos, remaining(pos), "<intr> ");		else			pos += snprintf(buf + pos, remaining(pos), "[%d] ",					task_pid_vnr(current));	}	pos_after_tid = pos;	if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)		pos += snprintf(buf + pos, remaining(pos), "%s:",				desc->modname);	if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)		pos += snprintf(buf + pos, remaining(pos), "%s:",				desc->function);	if (desc->flags & _DPRINTK_FLAGS_INCL_SOURCENAME)		pos += snprintf(buf + pos, remaining(pos), "%s:",				trim_prefix(desc->filename));	if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)		pos += snprintf(buf + pos, remaining(pos), "%d:",				desc->lineno);	if (pos - pos_after_tid)		pos += snprintf(buf + pos, remaining(pos), " ");	if (pos >= PREFIX_SIZE)		buf[PREFIX_SIZE - 1] = '\0'; 	return buf;} static inline char *dynamic_emit_prefix(struct _ddebug *desc, char *buf){	if (unlikely(desc->flags & _DPRINTK_FLAGS_INCL_ANY))		return __dynamic_emit_prefix(desc, buf);	return buf;} void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...){	va_list args;	struct va_format vaf;	char buf[PREFIX_SIZE] = ""; 	BUG_ON(!descriptor);	BUG_ON(!fmt); 	va_start(args, fmt); 	vaf.fmt = fmt;	vaf.va = &args; 	printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); 	va_end(args);}EXPORT_SYMBOL(__dynamic_pr_debug); void __dynamic_dev_dbg(struct _ddebug *descriptor,		      const struct device *dev, const char *fmt, ...){	struct va_format vaf;	va_list args; 	BUG_ON(!descriptor);	BUG_ON(!fmt); 	va_start(args, fmt); 	vaf.fmt = fmt;	vaf.va = &args; 	if (!dev) {		printk(KERN_DEBUG "(NULL device *): %pV", &vaf);	} else {		char buf[PREFIX_SIZE] = ""; 		dev_printk_emit(LOGLEVEL_DEBUG, dev, "%s%s %s: %pV",				dynamic_emit_prefix(descriptor, buf),				dev_driver_string(dev), dev_name(dev),				&vaf);	} 	va_end(args);}EXPORT_SYMBOL(__dynamic_dev_dbg); #ifdef CONFIG_NET void __dynamic_netdev_dbg(struct _ddebug *descriptor,			  const struct net_device *dev, const char *fmt, ...){	struct va_format vaf;	va_list args; 	BUG_ON(!descriptor);	BUG_ON(!fmt); 	va_start(args, fmt); 	vaf.fmt = fmt;	vaf.va = &args; 	if (dev && dev->dev.parent) {		char buf[PREFIX_SIZE] = ""; 		dev_printk_emit(LOGLEVEL_DEBUG, dev->dev.parent,				"%s%s %s %s%s: %pV",				dynamic_emit_prefix(descriptor, buf),				dev_driver_string(dev->dev.parent),				dev_name(dev->dev.parent),				netdev_name(dev), netdev_reg_state(dev),				&vaf);	} else if (dev) {		printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev),		       netdev_reg_state(dev), &vaf);	} else {		printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);	} 	va_end(args);}EXPORT_SYMBOL(__dynamic_netdev_dbg); #endif #if IS_ENABLED(CONFIG_INFINIBAND) void __dynamic_ibdev_dbg(struct _ddebug *descriptor,			 const struct ib_device *ibdev, const char *fmt, ...){	struct va_format vaf;	va_list args; 	va_start(args, fmt); 	vaf.fmt = fmt;	vaf.va = &args; 	if (ibdev && ibdev->dev.parent) {		char buf[PREFIX_SIZE] = ""; 		dev_printk_emit(LOGLEVEL_DEBUG, ibdev->dev.parent,				"%s%s %s %s: %pV",				dynamic_emit_prefix(descriptor, buf),				dev_driver_string(ibdev->dev.parent),				dev_name(ibdev->dev.parent),				dev_name(&ibdev->dev),				&vaf);	} else if (ibdev) {		printk(KERN_DEBUG "%s: %pV", dev_name(&ibdev->dev), &vaf);	} else {		printk(KERN_DEBUG "(NULL ib_device): %pV", &vaf);	} 	va_end(args);}EXPORT_SYMBOL(__dynamic_ibdev_dbg); #endif /* * Install a noop handler to make dyndbg look like a normal kernel cli param. * This avoids warnings about dyndbg being an unknown cli param when supplied * by a user. */static __init int dyndbg_setup(char *str){	return 1;} __setup("dyndbg=", dyndbg_setup); /*

Lines 501–1000 of 1495. Previous lines