static void w1_cn_callback()

in w1_netlink.c [535:711]


static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)
{
	struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);
	struct w1_slave *sl;
	struct w1_master *dev;
	u16 msg_len;
	u16 slave_len = 0;
	int err = 0;
	struct w1_cb_block *block = NULL;
	struct w1_cb_node *node = NULL;
	int node_count = 0;
	int cmd_count = 0;

	/* If any unknown flag is set let the application know, that way
	 * applications can detect the absence of features in kernels that
	 * don't know about them.  http://lwn.net/Articles/587527/
	 */
	if (cn->flags & ~(W1_CN_BUNDLE)) {
		w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL);
		return;
	}

	/* Count the number of master or slave commands there are to allocate
	 * space for one cb_node each.
	 */
	msg_len = cn->len;
	while (msg_len && !err) {
		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
			err = -E2BIG;
			break;
		}

		/* count messages for nodes and allocate any additional space
		 * required for slave lists
		 */
		if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) {
			++node_count;
			w1_list_count_cmds(msg, &cmd_count, &slave_len);
		}

		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
			sizeof(struct w1_netlink_msg) + msg->len);
	}
	msg = (struct w1_netlink_msg *)(cn + 1);
	if (node_count) {
		int size;
		int reply_size = sizeof(*cn) + cn->len + slave_len;
		if (cn->flags & W1_CN_BUNDLE) {
			/* bundling duplicats some of the messages */
			reply_size += 2 * cmd_count * (sizeof(struct cn_msg) +
				sizeof(struct w1_netlink_msg) +
				sizeof(struct w1_netlink_cmd));
		}
		reply_size = min(CONNECTOR_MAX_MSG_SIZE, reply_size);

		/* allocate space for the block, a copy of the original message,
		 * one node per cmd to point into the original message,
		 * space for replies which is the original message size plus
		 * space for any list slave data and status messages
		 * cn->len doesn't include itself which is part of the block
		 * */
		size =  /* block + original message */
			sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len +
			/* space for nodes */
			node_count * sizeof(struct w1_cb_node) +
			/* replies */
			sizeof(struct cn_msg) + reply_size;
		block = kzalloc(size, GFP_KERNEL);
		if (!block) {
			/* if the system is already out of memory,
			 * (A) will this work, and (B) would it be better
			 * to not try?
			 */
			w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
			return;
		}
		atomic_set(&block->refcnt, 1);
		block->portid = nsp->portid;
		memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len);
		node = (struct w1_cb_node *)(block->request_cn.data + cn->len);

		/* Sneeky, when not bundling, reply_size is the allocated space
		 * required for the reply, cn_msg isn't part of maxlen so
		 * it should be reply_size - sizeof(struct cn_msg), however
		 * when checking if there is enough space, w1_reply_make_space
		 * is called with the full message size including cn_msg,
		 * because it isn't known at that time if an additional cn_msg
		 * will need to be allocated.  So an extra cn_msg is added
		 * above in "size".
		 */
		block->maxlen = reply_size;
		block->first_cn = (struct cn_msg *)(node + node_count);
		memset(block->first_cn, 0, sizeof(*block->first_cn));
	}

	msg_len = cn->len;
	while (msg_len && !err) {

		dev = NULL;
		sl = NULL;

		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
			err = -E2BIG;
			break;
		}

		/* execute on this thread, no need to process later */
		if (msg->type == W1_LIST_MASTERS) {
			err = w1_process_command_root(cn, nsp->portid);
			goto out_cont;
		}

		/* All following message types require additional data,
		 * check here before references are taken.
		 */
		if (!msg->len) {
			err = -EPROTO;
			goto out_cont;
		}

		/* both search calls take references */
		if (msg->type == W1_MASTER_CMD) {
			dev = w1_search_master_id(msg->id.mst.id);
		} else if (msg->type == W1_SLAVE_CMD) {
			sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
			if (sl)
				dev = sl->master;
		} else {
			pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.\n",
				__func__, cn->id.idx, cn->id.val,
				msg->type, msg->len);
			err = -EPROTO;
			goto out_cont;
		}

		if (!dev) {
			err = -ENODEV;
			goto out_cont;
		}

		err = 0;

		atomic_inc(&block->refcnt);
		node->async.cb = w1_process_cb;
		node->block = block;
		node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
			(size_t)((u8 *)msg - (u8 *)cn));
		node->sl = sl;
		node->dev = dev;

		mutex_lock(&dev->list_mutex);
		list_add_tail(&node->async.async_entry, &dev->async_list);
		wake_up_process(dev->thread);
		mutex_unlock(&dev->list_mutex);
		++node;

out_cont:
		/* Can't queue because that modifies block and another
		 * thread could be processing the messages by now and
		 * there isn't a lock, send directly.
		 */
		if (err)
			w1_netlink_send_error(cn, msg, nsp->portid, err);
		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
			sizeof(struct w1_netlink_msg) + msg->len);

		/*
		 * Let's allow requests for nonexisting devices.
		 */
		if (err == -ENODEV)
			err = 0;
	}
	if (block)
		w1_unref_block(block);
}