diff options
author | Josef Bacik <josef@toxicpanda.com> | 2017-04-06 17:02:01 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2017-04-17 11:58:42 -0400 |
commit | b7aa3d39385dc2d95899f9e379623fef446a2acd (patch) | |
tree | ff0ff044ec1668138af9082d24305de7149b3628 | |
parent | e46c7287b1c27683a8e30ca825fb98e2b97f1099 (diff) |
nbd: add a reconfigure netlink command
We want to be able to reconnect dead connections to existing block
devices, so add a reconfigure netlink command. We will also allow users
to change their timeout on the fly, but everything else will require a
disconnect and reconnect. You won't be able to add more connections
either, simply replace dead connections with new more lively
connections.
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | drivers/block/nbd.c | 141 | ||||
-rw-r--r-- | include/uapi/linux/nbd-netlink.h | 1 |
2 files changed, 142 insertions, 0 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index efd2eba37c69..394ea891d909 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
@@ -785,6 +785,59 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg, | |||
785 | return 0; | 785 | return 0; |
786 | } | 786 | } |
787 | 787 | ||
788 | static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg) | ||
789 | { | ||
790 | struct nbd_config *config = nbd->config; | ||
791 | struct socket *sock, *old; | ||
792 | struct recv_thread_args *args; | ||
793 | int i; | ||
794 | int err; | ||
795 | |||
796 | sock = sockfd_lookup(arg, &err); | ||
797 | if (!sock) | ||
798 | return err; | ||
799 | |||
800 | args = kzalloc(sizeof(*args), GFP_KERNEL); | ||
801 | if (!args) { | ||
802 | sockfd_put(sock); | ||
803 | return -ENOMEM; | ||
804 | } | ||
805 | |||
806 | for (i = 0; i < config->num_connections; i++) { | ||
807 | struct nbd_sock *nsock = config->socks[i]; | ||
808 | |||
809 | if (!nsock->dead) | ||
810 | continue; | ||
811 | |||
812 | mutex_lock(&nsock->tx_lock); | ||
813 | if (!nsock->dead) { | ||
814 | mutex_unlock(&nsock->tx_lock); | ||
815 | continue; | ||
816 | } | ||
817 | sk_set_memalloc(sock->sk); | ||
818 | atomic_inc(&config->recv_threads); | ||
819 | refcount_inc(&nbd->config_refs); | ||
820 | old = nsock->sock; | ||
821 | nsock->fallback_index = -1; | ||
822 | nsock->sock = sock; | ||
823 | nsock->dead = false; | ||
824 | INIT_WORK(&args->work, recv_work); | ||
825 | args->index = i; | ||
826 | args->nbd = nbd; | ||
827 | mutex_unlock(&nsock->tx_lock); | ||
828 | sockfd_put(old); | ||
829 | |||
830 | /* We take the tx_mutex in an error path in the recv_work, so we | ||
831 | * need to queue_work outside of the tx_mutex. | ||
832 | */ | ||
833 | queue_work(recv_workqueue, &args->work); | ||
834 | return 0; | ||
835 | } | ||
836 | sockfd_put(sock); | ||
837 | kfree(args); | ||
838 | return -ENOSPC; | ||
839 | } | ||
840 | |||
788 | /* Reset all properties of an NBD device */ | 841 | /* Reset all properties of an NBD device */ |
789 | static void nbd_reset(struct nbd_device *nbd) | 842 | static void nbd_reset(struct nbd_device *nbd) |
790 | { | 843 | { |
@@ -1528,6 +1581,89 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) | |||
1528 | return 0; | 1581 | return 0; |
1529 | } | 1582 | } |
1530 | 1583 | ||
1584 | static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | ||
1585 | { | ||
1586 | struct nbd_device *nbd = NULL; | ||
1587 | struct nbd_config *config; | ||
1588 | int index; | ||
1589 | int ret = -EINVAL; | ||
1590 | |||
1591 | if (!netlink_capable(skb, CAP_SYS_ADMIN)) | ||
1592 | return -EPERM; | ||
1593 | |||
1594 | if (!info->attrs[NBD_ATTR_INDEX]) { | ||
1595 | printk(KERN_ERR "nbd: must specify a device to reconfigure\n"); | ||
1596 | return -EINVAL; | ||
1597 | } | ||
1598 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); | ||
1599 | mutex_lock(&nbd_index_mutex); | ||
1600 | nbd = idr_find(&nbd_index_idr, index); | ||
1601 | mutex_unlock(&nbd_index_mutex); | ||
1602 | if (!nbd) { | ||
1603 | printk(KERN_ERR "nbd: couldn't find a device at index %d\n", | ||
1604 | index); | ||
1605 | return -EINVAL; | ||
1606 | } | ||
1607 | |||
1608 | if (!refcount_inc_not_zero(&nbd->config_refs)) { | ||
1609 | dev_err(nbd_to_dev(nbd), | ||
1610 | "not configured, cannot reconfigure\n"); | ||
1611 | return -EINVAL; | ||
1612 | } | ||
1613 | |||
1614 | mutex_lock(&nbd->config_lock); | ||
1615 | config = nbd->config; | ||
1616 | if (!test_bit(NBD_BOUND, &config->runtime_flags) || | ||
1617 | !nbd->task_recv) { | ||
1618 | dev_err(nbd_to_dev(nbd), | ||
1619 | "not configured, cannot reconfigure\n"); | ||
1620 | goto out; | ||
1621 | } | ||
1622 | |||
1623 | if (info->attrs[NBD_ATTR_TIMEOUT]) { | ||
1624 | u64 timeout = nla_get_u64(info->attrs[NBD_ATTR_TIMEOUT]); | ||
1625 | nbd->tag_set.timeout = timeout * HZ; | ||
1626 | blk_queue_rq_timeout(nbd->disk->queue, timeout * HZ); | ||
1627 | } | ||
1628 | |||
1629 | if (info->attrs[NBD_ATTR_SOCKETS]) { | ||
1630 | struct nlattr *attr; | ||
1631 | int rem, fd; | ||
1632 | |||
1633 | nla_for_each_nested(attr, info->attrs[NBD_ATTR_SOCKETS], | ||
1634 | rem) { | ||
1635 | struct nlattr *socks[NBD_SOCK_MAX+1]; | ||
1636 | |||
1637 | if (nla_type(attr) != NBD_SOCK_ITEM) { | ||
1638 | printk(KERN_ERR "nbd: socks must be embedded in a SOCK_ITEM attr\n"); | ||
1639 | ret = -EINVAL; | ||
1640 | goto out; | ||
1641 | } | ||
1642 | ret = nla_parse_nested(socks, NBD_SOCK_MAX, attr, | ||
1643 | nbd_sock_policy); | ||
1644 | if (ret != 0) { | ||
1645 | printk(KERN_ERR "nbd: error processing sock list\n"); | ||
1646 | ret = -EINVAL; | ||
1647 | goto out; | ||
1648 | } | ||
1649 | if (!socks[NBD_SOCK_FD]) | ||
1650 | continue; | ||
1651 | fd = (int)nla_get_u32(socks[NBD_SOCK_FD]); | ||
1652 | ret = nbd_reconnect_socket(nbd, fd); | ||
1653 | if (ret) { | ||
1654 | if (ret == -ENOSPC) | ||
1655 | ret = 0; | ||
1656 | goto out; | ||
1657 | } | ||
1658 | dev_info(nbd_to_dev(nbd), "reconnected socket\n"); | ||
1659 | } | ||
1660 | } | ||
1661 | out: | ||
1662 | mutex_unlock(&nbd->config_lock); | ||
1663 | nbd_config_put(nbd); | ||
1664 | return ret; | ||
1665 | } | ||
1666 | |||
1531 | static const struct genl_ops nbd_connect_genl_ops[] = { | 1667 | static const struct genl_ops nbd_connect_genl_ops[] = { |
1532 | { | 1668 | { |
1533 | .cmd = NBD_CMD_CONNECT, | 1669 | .cmd = NBD_CMD_CONNECT, |
@@ -1539,6 +1675,11 @@ static const struct genl_ops nbd_connect_genl_ops[] = { | |||
1539 | .policy = nbd_attr_policy, | 1675 | .policy = nbd_attr_policy, |
1540 | .doit = nbd_genl_disconnect, | 1676 | .doit = nbd_genl_disconnect, |
1541 | }, | 1677 | }, |
1678 | { | ||
1679 | .cmd = NBD_CMD_RECONFIGURE, | ||
1680 | .policy = nbd_attr_policy, | ||
1681 | .doit = nbd_genl_reconfigure, | ||
1682 | }, | ||
1542 | }; | 1683 | }; |
1543 | 1684 | ||
1544 | static struct genl_family nbd_genl_family __ro_after_init = { | 1685 | static struct genl_family nbd_genl_family __ro_after_init = { |
diff --git a/include/uapi/linux/nbd-netlink.h b/include/uapi/linux/nbd-netlink.h index fd0f4e45f03e..f932f96a7c2f 100644 --- a/include/uapi/linux/nbd-netlink.h +++ b/include/uapi/linux/nbd-netlink.h | |||
@@ -62,6 +62,7 @@ enum { | |||
62 | NBD_CMD_UNSPEC, | 62 | NBD_CMD_UNSPEC, |
63 | NBD_CMD_CONNECT, | 63 | NBD_CMD_CONNECT, |
64 | NBD_CMD_DISCONNECT, | 64 | NBD_CMD_DISCONNECT, |
65 | NBD_CMD_RECONFIGURE, | ||
65 | __NBD_CMD_MAX, | 66 | __NBD_CMD_MAX, |
66 | }; | 67 | }; |
67 | #define NBD_CMD_MAX (__NBD_CMD_MAX - 1) | 68 | #define NBD_CMD_MAX (__NBD_CMD_MAX - 1) |