in nbd.c [1907:2078]
static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
{
struct nbd_device *nbd;
struct nbd_config *config;
int index = -1;
int ret;
bool put_dev = false;
if (!netlink_capable(skb, CAP_SYS_ADMIN))
return -EPERM;
if (info->attrs[NBD_ATTR_INDEX])
index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]);
if (!info->attrs[NBD_ATTR_SOCKETS]) {
printk(KERN_ERR "nbd: must specify at least one socket\n");
return -EINVAL;
}
if (!info->attrs[NBD_ATTR_SIZE_BYTES]) {
printk(KERN_ERR "nbd: must specify a size in bytes for the device\n");
return -EINVAL;
}
again:
mutex_lock(&nbd_index_mutex);
if (index == -1) {
nbd = nbd_find_get_unused();
} else {
nbd = idr_find(&nbd_index_idr, index);
if (nbd) {
if ((test_bit(NBD_DESTROY_ON_DISCONNECT, &nbd->flags) &&
test_bit(NBD_DISCONNECT_REQUESTED, &nbd->flags)) ||
!refcount_inc_not_zero(&nbd->refs)) {
mutex_unlock(&nbd_index_mutex);
pr_err("nbd: device at index %d is going down\n",
index);
return -EINVAL;
}
}
}
mutex_unlock(&nbd_index_mutex);
if (!nbd) {
nbd = nbd_dev_add(index, 2);
if (IS_ERR(nbd)) {
pr_err("nbd: failed to add new device\n");
return PTR_ERR(nbd);
}
}
mutex_lock(&nbd->config_lock);
if (refcount_read(&nbd->config_refs)) {
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
if (index == -1)
goto again;
printk(KERN_ERR "nbd: nbd%d already in use\n", index);
return -EBUSY;
}
if (WARN_ON(nbd->config)) {
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
return -EINVAL;
}
config = nbd->config = nbd_alloc_config();
if (!nbd->config) {
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
printk(KERN_ERR "nbd: couldn't allocate config\n");
return -ENOMEM;
}
refcount_set(&nbd->config_refs, 1);
set_bit(NBD_RT_BOUND, &config->runtime_flags);
ret = nbd_genl_size_set(info, nbd);
if (ret)
goto out;
if (info->attrs[NBD_ATTR_TIMEOUT])
nbd_set_cmd_timeout(nbd,
nla_get_u64(info->attrs[NBD_ATTR_TIMEOUT]));
if (info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]) {
config->dead_conn_timeout =
nla_get_u64(info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]);
config->dead_conn_timeout *= HZ;
}
if (info->attrs[NBD_ATTR_SERVER_FLAGS])
config->flags =
nla_get_u64(info->attrs[NBD_ATTR_SERVER_FLAGS]);
if (info->attrs[NBD_ATTR_CLIENT_FLAGS]) {
u64 flags = nla_get_u64(info->attrs[NBD_ATTR_CLIENT_FLAGS]);
if (flags & NBD_CFLAG_DESTROY_ON_DISCONNECT) {
/*
* We have 1 ref to keep the device around, and then 1
* ref for our current operation here, which will be
* inherited by the config. If we already have
* DESTROY_ON_DISCONNECT set then we know we don't have
* that extra ref already held so we don't need the
* put_dev.
*/
if (!test_and_set_bit(NBD_DESTROY_ON_DISCONNECT,
&nbd->flags))
put_dev = true;
} else {
if (test_and_clear_bit(NBD_DESTROY_ON_DISCONNECT,
&nbd->flags))
refcount_inc(&nbd->refs);
}
if (flags & NBD_CFLAG_DISCONNECT_ON_CLOSE) {
set_bit(NBD_RT_DISCONNECT_ON_CLOSE,
&config->runtime_flags);
}
}
if (info->attrs[NBD_ATTR_SOCKETS]) {
struct nlattr *attr;
int rem, fd;
nla_for_each_nested(attr, info->attrs[NBD_ATTR_SOCKETS],
rem) {
struct nlattr *socks[NBD_SOCK_MAX+1];
if (nla_type(attr) != NBD_SOCK_ITEM) {
printk(KERN_ERR "nbd: socks must be embedded in a SOCK_ITEM attr\n");
ret = -EINVAL;
goto out;
}
ret = nla_parse_nested_deprecated(socks, NBD_SOCK_MAX,
attr,
nbd_sock_policy,
info->extack);
if (ret != 0) {
printk(KERN_ERR "nbd: error processing sock list\n");
ret = -EINVAL;
goto out;
}
if (!socks[NBD_SOCK_FD])
continue;
fd = (int)nla_get_u32(socks[NBD_SOCK_FD]);
ret = nbd_add_socket(nbd, fd, true);
if (ret)
goto out;
}
}
ret = nbd_start_device(nbd);
if (ret)
goto out;
if (info->attrs[NBD_ATTR_BACKEND_IDENTIFIER]) {
nbd->backend = nla_strdup(info->attrs[NBD_ATTR_BACKEND_IDENTIFIER],
GFP_KERNEL);
if (!nbd->backend) {
ret = -ENOMEM;
goto out;
}
}
ret = device_create_file(disk_to_dev(nbd->disk), &backend_attr);
if (ret) {
dev_err(disk_to_dev(nbd->disk),
"device_create_file failed for backend!\n");
goto out;
}
set_bit(NBD_RT_HAS_BACKEND_FILE, &config->runtime_flags);
out:
mutex_unlock(&nbd->config_lock);
if (!ret) {
set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
refcount_inc(&nbd->config_refs);
nbd_connect_reply(info, nbd->index);
}
nbd_config_put(nbd);
if (put_dev)
nbd_put(nbd);
return ret;
}