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);
}