| /* | 
 |  * What:		/sys/kernel/debug/orangefs/debug-help | 
 |  * Date:		June 2015 | 
 |  * Contact:		Mike Marshall <hubcap@omnibond.com> | 
 |  * Description: | 
 |  * 			List of client and kernel debug keywords. | 
 |  * | 
 |  * | 
 |  * What:		/sys/kernel/debug/orangefs/client-debug | 
 |  * Date:		June 2015 | 
 |  * Contact:		Mike Marshall <hubcap@omnibond.com> | 
 |  * Description: | 
 |  * 			Debug setting for "the client", the userspace | 
 |  * 			helper for the kernel module. | 
 |  * | 
 |  * | 
 |  * What:		/sys/kernel/debug/orangefs/kernel-debug | 
 |  * Date:		June 2015 | 
 |  * Contact:		Mike Marshall <hubcap@omnibond.com> | 
 |  * Description: | 
 |  * 			Debug setting for the orangefs kernel module. | 
 |  * | 
 |  * 			Any of the keywords, or comma-separated lists | 
 |  * 			of keywords, from debug-help can be catted to | 
 |  * 			client-debug or kernel-debug. | 
 |  * | 
 |  * 			"none", "all" and "verbose" are special keywords | 
 |  * 			for client-debug. Setting client-debug to "all" | 
 |  * 			is kind of like trying to drink water from a | 
 |  * 			fire hose, "verbose" triggers most of the same | 
 |  * 			output except for the constant flow of output | 
 |  * 			from the main wait loop. | 
 |  * | 
 |  * 			"none" and "all" are similar settings for kernel-debug | 
 |  * 			no need for a "verbose". | 
 |  */ | 
 | #include <linux/debugfs.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | #include <linux/uaccess.h> | 
 |  | 
 | #include "orangefs-debugfs.h" | 
 | #include "protocol.h" | 
 | #include "orangefs-kernel.h" | 
 |  | 
 | static int orangefs_debug_disabled = 1; | 
 |  | 
 | static int orangefs_debug_help_open(struct inode *, struct file *); | 
 |  | 
 | const struct file_operations debug_help_fops = { | 
 | 	.open           = orangefs_debug_help_open, | 
 | 	.read           = seq_read, | 
 | 	.release        = seq_release, | 
 | 	.llseek         = seq_lseek, | 
 | }; | 
 |  | 
 | static void *help_start(struct seq_file *, loff_t *); | 
 | static void *help_next(struct seq_file *, void *, loff_t *); | 
 | static void help_stop(struct seq_file *, void *); | 
 | static int help_show(struct seq_file *, void *); | 
 |  | 
 | static const struct seq_operations help_debug_ops = { | 
 | 	.start	= help_start, | 
 | 	.next	= help_next, | 
 | 	.stop	= help_stop, | 
 | 	.show	= help_show, | 
 | }; | 
 |  | 
 | /* | 
 |  * Used to protect data in ORANGEFS_KMOD_DEBUG_FILE and | 
 |  * ORANGEFS_KMOD_DEBUG_FILE. | 
 |  */ | 
 | static DEFINE_MUTEX(orangefs_debug_lock); | 
 |  | 
 | int orangefs_debug_open(struct inode *, struct file *); | 
 |  | 
 | static ssize_t orangefs_debug_read(struct file *, | 
 | 				 char __user *, | 
 | 				 size_t, | 
 | 				 loff_t *); | 
 |  | 
 | static ssize_t orangefs_debug_write(struct file *, | 
 | 				  const char __user *, | 
 | 				  size_t, | 
 | 				  loff_t *); | 
 |  | 
 | static const struct file_operations kernel_debug_fops = { | 
 | 	.open           = orangefs_debug_open, | 
 | 	.read           = orangefs_debug_read, | 
 | 	.write		= orangefs_debug_write, | 
 | 	.llseek         = generic_file_llseek, | 
 | }; | 
 |  | 
 | /* | 
 |  * initialize kmod debug operations, create orangefs debugfs dir and | 
 |  * ORANGEFS_KMOD_DEBUG_HELP_FILE. | 
 |  */ | 
 | int orangefs_debugfs_init(void) | 
 | { | 
 |  | 
 | 	int rc = -ENOMEM; | 
 |  | 
 | 	debug_dir = debugfs_create_dir("orangefs", NULL); | 
 | 	if (!debug_dir) { | 
 | 		pr_info("%s: debugfs_create_dir failed.\n", __func__); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	help_file_dentry = debugfs_create_file(ORANGEFS_KMOD_DEBUG_HELP_FILE, | 
 | 				  0444, | 
 | 				  debug_dir, | 
 | 				  debug_help_string, | 
 | 				  &debug_help_fops); | 
 | 	if (!help_file_dentry) { | 
 | 		pr_info("%s: debugfs_create_file failed.\n", __func__); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	orangefs_debug_disabled = 0; | 
 | 	rc = 0; | 
 |  | 
 | out: | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | void orangefs_debugfs_cleanup(void) | 
 | { | 
 | 	debugfs_remove_recursive(debug_dir); | 
 | } | 
 |  | 
 | /* open ORANGEFS_KMOD_DEBUG_HELP_FILE */ | 
 | static int orangefs_debug_help_open(struct inode *inode, struct file *file) | 
 | { | 
 | 	int rc = -ENODEV; | 
 | 	int ret; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "orangefs_debug_help_open: start\n"); | 
 |  | 
 | 	if (orangefs_debug_disabled) | 
 | 		goto out; | 
 |  | 
 | 	ret = seq_open(file, &help_debug_ops); | 
 | 	if (ret) | 
 | 		goto out; | 
 |  | 
 | 	((struct seq_file *)(file->private_data))->private = inode->i_private; | 
 |  | 
 | 	rc = 0; | 
 |  | 
 | out: | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "orangefs_debug_help_open: rc:%d:\n", | 
 | 		     rc); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* | 
 |  * I think start always gets called again after stop. Start | 
 |  * needs to return NULL when it is done. The whole "payload" | 
 |  * in this case is a single (long) string, so by the second | 
 |  * time we get to start (pos = 1), we're done. | 
 |  */ | 
 | static void *help_start(struct seq_file *m, loff_t *pos) | 
 | { | 
 | 	void *payload = NULL; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_start: start\n"); | 
 |  | 
 | 	if (*pos == 0) | 
 | 		payload = m->private; | 
 |  | 
 | 	return payload; | 
 | } | 
 |  | 
 | static void *help_next(struct seq_file *m, void *v, loff_t *pos) | 
 | { | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_next: start\n"); | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static void help_stop(struct seq_file *m, void *p) | 
 | { | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_stop: start\n"); | 
 | } | 
 |  | 
 | static int help_show(struct seq_file *m, void *v) | 
 | { | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_show: start\n"); | 
 |  | 
 | 	seq_puts(m, v); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * initialize the kernel-debug file. | 
 |  */ | 
 | int orangefs_kernel_debug_init(void) | 
 | { | 
 | 	int rc = -ENOMEM; | 
 | 	struct dentry *ret; | 
 | 	char *k_buffer = NULL; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); | 
 |  | 
 | 	k_buffer = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | 
 | 	if (!k_buffer) | 
 | 		goto out; | 
 |  | 
 | 	if (strlen(kernel_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { | 
 | 		strcpy(k_buffer, kernel_debug_string); | 
 | 		strcat(k_buffer, "\n"); | 
 | 	} else { | 
 | 		strcpy(k_buffer, "none\n"); | 
 | 		pr_info("%s: overflow 1!\n", __func__); | 
 | 	} | 
 |  | 
 | 	ret = debugfs_create_file(ORANGEFS_KMOD_DEBUG_FILE, | 
 | 				  0444, | 
 | 				  debug_dir, | 
 | 				  k_buffer, | 
 | 				  &kernel_debug_fops); | 
 | 	if (!ret) { | 
 | 		pr_info("%s: failed to create %s.\n", | 
 | 			__func__, | 
 | 			ORANGEFS_KMOD_DEBUG_FILE); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	rc = 0; | 
 |  | 
 | out: | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: rc:%d:\n", __func__, rc); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* | 
 |  * initialize the client-debug file. | 
 |  */ | 
 | int orangefs_client_debug_init(void) | 
 | { | 
 |  | 
 | 	int rc = -ENOMEM; | 
 | 	char *c_buffer = NULL; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); | 
 |  | 
 | 	c_buffer = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | 
 | 	if (!c_buffer) | 
 | 		goto out; | 
 |  | 
 | 	if (strlen(client_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { | 
 | 		strcpy(c_buffer, client_debug_string); | 
 | 		strcat(c_buffer, "\n"); | 
 | 	} else { | 
 | 		strcpy(c_buffer, "none\n"); | 
 | 		pr_info("%s: overflow! 2\n", __func__); | 
 | 	} | 
 |  | 
 | 	client_debug_dentry = debugfs_create_file(ORANGEFS_CLIENT_DEBUG_FILE, | 
 | 						  0444, | 
 | 						  debug_dir, | 
 | 						  c_buffer, | 
 | 						  &kernel_debug_fops); | 
 | 	if (!client_debug_dentry) { | 
 | 		pr_info("%s: failed to create updated %s.\n", | 
 | 			__func__, | 
 | 			ORANGEFS_CLIENT_DEBUG_FILE); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	rc = 0; | 
 |  | 
 | out: | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: rc:%d:\n", __func__, rc); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* open ORANGEFS_KMOD_DEBUG_FILE or ORANGEFS_CLIENT_DEBUG_FILE.*/ | 
 | int orangefs_debug_open(struct inode *inode, struct file *file) | 
 | { | 
 | 	int rc = -ENODEV; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "%s: orangefs_debug_disabled: %d\n", | 
 | 		     __func__, | 
 | 		     orangefs_debug_disabled); | 
 |  | 
 | 	if (orangefs_debug_disabled) | 
 | 		goto out; | 
 |  | 
 | 	rc = 0; | 
 | 	mutex_lock(&orangefs_debug_lock); | 
 | 	file->private_data = inode->i_private; | 
 | 	mutex_unlock(&orangefs_debug_lock); | 
 |  | 
 | out: | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "orangefs_debug_open: rc: %d\n", | 
 | 		     rc); | 
 | 	return rc; | 
 | } | 
 |  | 
 | static ssize_t orangefs_debug_read(struct file *file, | 
 | 				 char __user *ubuf, | 
 | 				 size_t count, | 
 | 				 loff_t *ppos) | 
 | { | 
 | 	char *buf; | 
 | 	int sprintf_ret; | 
 | 	ssize_t read_ret = -ENOMEM; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, "orangefs_debug_read: start\n"); | 
 |  | 
 | 	buf = kmalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | 
 | 	if (!buf) | 
 | 		goto out; | 
 |  | 
 | 	mutex_lock(&orangefs_debug_lock); | 
 | 	sprintf_ret = sprintf(buf, "%s", (char *)file->private_data); | 
 | 	mutex_unlock(&orangefs_debug_lock); | 
 |  | 
 | 	read_ret = simple_read_from_buffer(ubuf, count, ppos, buf, sprintf_ret); | 
 |  | 
 | 	kfree(buf); | 
 |  | 
 | out: | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "orangefs_debug_read: ret: %zu\n", | 
 | 		     read_ret); | 
 |  | 
 | 	return read_ret; | 
 | } | 
 |  | 
 | static ssize_t orangefs_debug_write(struct file *file, | 
 | 				  const char __user *ubuf, | 
 | 				  size_t count, | 
 | 				  loff_t *ppos) | 
 | { | 
 | 	char *buf; | 
 | 	int rc = -EFAULT; | 
 | 	size_t silly = 0; | 
 | 	char *debug_string; | 
 | 	struct orangefs_kernel_op_s *new_op = NULL; | 
 | 	struct client_debug_mask c_mask = { NULL, 0, 0 }; | 
 |  | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		"orangefs_debug_write: %s\n", | 
 | 		file->f_path.dentry->d_name.name); | 
 |  | 
 | 	/* | 
 | 	 * Thwart users who try to jamb a ridiculous number | 
 | 	 * of bytes into the debug file... | 
 | 	 */ | 
 | 	if (count > ORANGEFS_MAX_DEBUG_STRING_LEN + 1) { | 
 | 		silly = count; | 
 | 		count = ORANGEFS_MAX_DEBUG_STRING_LEN + 1; | 
 | 	} | 
 |  | 
 | 	buf = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | 
 | 	if (!buf) | 
 | 		goto out; | 
 |  | 
 | 	if (copy_from_user(buf, ubuf, count - 1)) { | 
 | 		gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 			     "%s: copy_from_user failed!\n", | 
 | 			     __func__); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Map the keyword string from userspace into a valid debug mask. | 
 | 	 * The mapping process involves mapping the human-inputted string | 
 | 	 * into a valid mask, and then rebuilding the string from the | 
 | 	 * verified valid mask. | 
 | 	 * | 
 | 	 * A service operation is required to set a new client-side | 
 | 	 * debug mask. | 
 | 	 */ | 
 | 	if (!strcmp(file->f_path.dentry->d_name.name, | 
 | 		    ORANGEFS_KMOD_DEBUG_FILE)) { | 
 | 		debug_string_to_mask(buf, &gossip_debug_mask, 0); | 
 | 		debug_mask_to_string(&gossip_debug_mask, 0); | 
 | 		debug_string = kernel_debug_string; | 
 | 		gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 			     "New kernel debug string is %s\n", | 
 | 			     kernel_debug_string); | 
 | 	} else { | 
 | 		/* Can't reset client debug mask if client is not running. */ | 
 | 		if (is_daemon_in_service()) { | 
 | 			pr_info("%s: Client not running :%d:\n", | 
 | 				__func__, | 
 | 				is_daemon_in_service()); | 
 | 			goto out; | 
 | 		} | 
 |  | 
 | 		debug_string_to_mask(buf, &c_mask, 1); | 
 | 		debug_mask_to_string(&c_mask, 1); | 
 | 		debug_string = client_debug_string; | 
 |  | 
 | 		new_op = op_alloc(ORANGEFS_VFS_OP_PARAM); | 
 | 		if (!new_op) { | 
 | 			pr_info("%s: op_alloc failed!\n", __func__); | 
 | 			goto out; | 
 | 		} | 
 |  | 
 | 		new_op->upcall.req.param.op = | 
 | 			ORANGEFS_PARAM_REQUEST_OP_TWO_MASK_VALUES; | 
 | 		new_op->upcall.req.param.type = ORANGEFS_PARAM_REQUEST_SET; | 
 | 		memset(new_op->upcall.req.param.s_value, | 
 | 		       0, | 
 | 		       ORANGEFS_MAX_DEBUG_STRING_LEN); | 
 | 		sprintf(new_op->upcall.req.param.s_value, | 
 | 			"%llx %llx\n", | 
 | 			c_mask.mask1, | 
 | 			c_mask.mask2); | 
 |  | 
 | 		/* service_operation returns 0 on success... */ | 
 | 		rc = service_operation(new_op, | 
 | 				       "orangefs_param", | 
 | 					ORANGEFS_OP_INTERRUPTIBLE); | 
 |  | 
 | 		if (rc) | 
 | 			gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 				     "%s: service_operation failed! rc:%d:\n", | 
 | 				     __func__, | 
 | 				     rc); | 
 |  | 
 | 		op_release(new_op); | 
 | 	} | 
 |  | 
 | 	mutex_lock(&orangefs_debug_lock); | 
 | 	memset(file->f_inode->i_private, 0, ORANGEFS_MAX_DEBUG_STRING_LEN); | 
 | 	sprintf((char *)file->f_inode->i_private, "%s\n", debug_string); | 
 | 	mutex_unlock(&orangefs_debug_lock); | 
 |  | 
 | 	*ppos += count; | 
 | 	if (silly) | 
 | 		rc = silly; | 
 | 	else | 
 | 		rc = count; | 
 |  | 
 | out: | 
 | 	gossip_debug(GOSSIP_DEBUGFS_DEBUG, | 
 | 		     "orangefs_debug_write: rc: %d\n", | 
 | 		     rc); | 
 | 	kfree(buf); | 
 | 	return rc; | 
 | } |