diff options
-rw-r--r-- | drivers/block/nbd.c | 108 | ||||
-rw-r--r-- | include/uapi/linux/nbd-netlink.h | 25 |
2 files changed, 133 insertions, 0 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index c5f866bcfea6..cb45d799bc5c 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
@@ -45,6 +45,7 @@ | |||
45 | 45 | ||
46 | static DEFINE_IDR(nbd_index_idr); | 46 | static DEFINE_IDR(nbd_index_idr); |
47 | static DEFINE_MUTEX(nbd_index_mutex); | 47 | static DEFINE_MUTEX(nbd_index_mutex); |
48 | static int nbd_total_devices = 0; | ||
48 | 49 | ||
49 | struct nbd_sock { | 50 | struct nbd_sock { |
50 | struct socket *sock; | 51 | struct socket *sock; |
@@ -130,6 +131,7 @@ static int nbd_dev_dbg_init(struct nbd_device *nbd); | |||
130 | static void nbd_dev_dbg_close(struct nbd_device *nbd); | 131 | static void nbd_dev_dbg_close(struct nbd_device *nbd); |
131 | static void nbd_config_put(struct nbd_device *nbd); | 132 | static void nbd_config_put(struct nbd_device *nbd); |
132 | static void nbd_connect_reply(struct genl_info *info, int index); | 133 | static void nbd_connect_reply(struct genl_info *info, int index); |
134 | static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info); | ||
133 | static void nbd_dead_link_work(struct work_struct *work); | 135 | static void nbd_dead_link_work(struct work_struct *work); |
134 | 136 | ||
135 | static inline struct device *nbd_to_dev(struct nbd_device *nbd) | 137 | static inline struct device *nbd_to_dev(struct nbd_device *nbd) |
@@ -1457,6 +1459,7 @@ static int nbd_dev_add(int index) | |||
1457 | sprintf(disk->disk_name, "nbd%d", index); | 1459 | sprintf(disk->disk_name, "nbd%d", index); |
1458 | nbd_reset(nbd); | 1460 | nbd_reset(nbd); |
1459 | add_disk(disk); | 1461 | add_disk(disk); |
1462 | nbd_total_devices++; | ||
1460 | return index; | 1463 | return index; |
1461 | 1464 | ||
1462 | out_free_tags: | 1465 | out_free_tags: |
@@ -1493,12 +1496,22 @@ static struct nla_policy nbd_attr_policy[NBD_ATTR_MAX + 1] = { | |||
1493 | [NBD_ATTR_CLIENT_FLAGS] = { .type = NLA_U64 }, | 1496 | [NBD_ATTR_CLIENT_FLAGS] = { .type = NLA_U64 }, |
1494 | [NBD_ATTR_SOCKETS] = { .type = NLA_NESTED}, | 1497 | [NBD_ATTR_SOCKETS] = { .type = NLA_NESTED}, |
1495 | [NBD_ATTR_DEAD_CONN_TIMEOUT] = { .type = NLA_U64 }, | 1498 | [NBD_ATTR_DEAD_CONN_TIMEOUT] = { .type = NLA_U64 }, |
1499 | [NBD_ATTR_DEVICE_LIST] = { .type = NLA_NESTED}, | ||
1496 | }; | 1500 | }; |
1497 | 1501 | ||
1498 | static struct nla_policy nbd_sock_policy[NBD_SOCK_MAX + 1] = { | 1502 | static struct nla_policy nbd_sock_policy[NBD_SOCK_MAX + 1] = { |
1499 | [NBD_SOCK_FD] = { .type = NLA_U32 }, | 1503 | [NBD_SOCK_FD] = { .type = NLA_U32 }, |
1500 | }; | 1504 | }; |
1501 | 1505 | ||
1506 | /* We don't use this right now since we don't parse the incoming list, but we | ||
1507 | * still want it here so userspace knows what to expect. | ||
1508 | */ | ||
1509 | static struct nla_policy __attribute__((unused)) | ||
1510 | nbd_device_policy[NBD_DEVICE_ATTR_MAX + 1] = { | ||
1511 | [NBD_DEVICE_INDEX] = { .type = NLA_U32 }, | ||
1512 | [NBD_DEVICE_CONNECTED] = { .type = NLA_U8 }, | ||
1513 | }; | ||
1514 | |||
1502 | static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) | 1515 | static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) |
1503 | { | 1516 | { |
1504 | struct nbd_device *nbd = NULL; | 1517 | struct nbd_device *nbd = NULL; |
@@ -1764,6 +1777,11 @@ static const struct genl_ops nbd_connect_genl_ops[] = { | |||
1764 | .policy = nbd_attr_policy, | 1777 | .policy = nbd_attr_policy, |
1765 | .doit = nbd_genl_reconfigure, | 1778 | .doit = nbd_genl_reconfigure, |
1766 | }, | 1779 | }, |
1780 | { | ||
1781 | .cmd = NBD_CMD_STATUS, | ||
1782 | .policy = nbd_attr_policy, | ||
1783 | .doit = nbd_genl_status, | ||
1784 | }, | ||
1767 | }; | 1785 | }; |
1768 | 1786 | ||
1769 | static const struct genl_multicast_group nbd_mcast_grps[] = { | 1787 | static const struct genl_multicast_group nbd_mcast_grps[] = { |
@@ -1782,6 +1800,96 @@ static struct genl_family nbd_genl_family __ro_after_init = { | |||
1782 | .n_mcgrps = ARRAY_SIZE(nbd_mcast_grps), | 1800 | .n_mcgrps = ARRAY_SIZE(nbd_mcast_grps), |
1783 | }; | 1801 | }; |
1784 | 1802 | ||
1803 | static int populate_nbd_status(struct nbd_device *nbd, struct sk_buff *reply) | ||
1804 | { | ||
1805 | struct nlattr *dev_opt; | ||
1806 | u8 connected = 0; | ||
1807 | int ret; | ||
1808 | |||
1809 | /* This is a little racey, but for status it's ok. The | ||
1810 | * reason we don't take a ref here is because we can't | ||
1811 | * take a ref in the index == -1 case as we would need | ||
1812 | * to put under the nbd_index_mutex, which could | ||
1813 | * deadlock if we are configured to remove ourselves | ||
1814 | * once we're disconnected. | ||
1815 | */ | ||
1816 | if (refcount_read(&nbd->config_refs)) | ||
1817 | connected = 1; | ||
1818 | dev_opt = nla_nest_start(reply, NBD_DEVICE_ITEM); | ||
1819 | if (!dev_opt) | ||
1820 | return -EMSGSIZE; | ||
1821 | ret = nla_put_u32(reply, NBD_DEVICE_INDEX, nbd->index); | ||
1822 | if (ret) | ||
1823 | return -EMSGSIZE; | ||
1824 | ret = nla_put_u8(reply, NBD_DEVICE_CONNECTED, | ||
1825 | connected); | ||
1826 | if (ret) | ||
1827 | return -EMSGSIZE; | ||
1828 | nla_nest_end(reply, dev_opt); | ||
1829 | return 0; | ||
1830 | } | ||
1831 | |||
1832 | static int status_cb(int id, void *ptr, void *data) | ||
1833 | { | ||
1834 | struct nbd_device *nbd = ptr; | ||
1835 | return populate_nbd_status(nbd, (struct sk_buff *)data); | ||
1836 | } | ||
1837 | |||
1838 | static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info) | ||
1839 | { | ||
1840 | struct nlattr *dev_list; | ||
1841 | struct sk_buff *reply; | ||
1842 | void *reply_head; | ||
1843 | size_t msg_size; | ||
1844 | int index = -1; | ||
1845 | int ret = -ENOMEM; | ||
1846 | |||
1847 | if (info->attrs[NBD_ATTR_INDEX]) | ||
1848 | index = nla_get_u32(info->attrs[NBD_ATTR_INDEX]); | ||
1849 | |||
1850 | mutex_lock(&nbd_index_mutex); | ||
1851 | |||
1852 | msg_size = nla_total_size(nla_attr_size(sizeof(u32)) + | ||
1853 | nla_attr_size(sizeof(u8))); | ||
1854 | msg_size *= (index == -1) ? nbd_total_devices : 1; | ||
1855 | |||
1856 | reply = genlmsg_new(msg_size, GFP_KERNEL); | ||
1857 | if (!reply) | ||
1858 | goto out; | ||
1859 | reply_head = genlmsg_put_reply(reply, info, &nbd_genl_family, 0, | ||
1860 | NBD_CMD_STATUS); | ||
1861 | if (!reply_head) { | ||
1862 | nlmsg_free(reply); | ||
1863 | goto out; | ||
1864 | } | ||
1865 | |||
1866 | dev_list = nla_nest_start(reply, NBD_ATTR_DEVICE_LIST); | ||
1867 | if (index == -1) { | ||
1868 | ret = idr_for_each(&nbd_index_idr, &status_cb, reply); | ||
1869 | if (ret) { | ||
1870 | nlmsg_free(reply); | ||
1871 | goto out; | ||
1872 | } | ||
1873 | } else { | ||
1874 | struct nbd_device *nbd; | ||
1875 | nbd = idr_find(&nbd_index_idr, index); | ||
1876 | if (nbd) { | ||
1877 | ret = populate_nbd_status(nbd, reply); | ||
1878 | if (ret) { | ||
1879 | nlmsg_free(reply); | ||
1880 | goto out; | ||
1881 | } | ||
1882 | } | ||
1883 | } | ||
1884 | nla_nest_end(reply, dev_list); | ||
1885 | genlmsg_end(reply, reply_head); | ||
1886 | genlmsg_reply(reply, info); | ||
1887 | ret = 0; | ||
1888 | out: | ||
1889 | mutex_unlock(&nbd_index_mutex); | ||
1890 | return ret; | ||
1891 | } | ||
1892 | |||
1785 | static void nbd_connect_reply(struct genl_info *info, int index) | 1893 | static void nbd_connect_reply(struct genl_info *info, int index) |
1786 | { | 1894 | { |
1787 | struct sk_buff *skb; | 1895 | struct sk_buff *skb; |
diff --git a/include/uapi/linux/nbd-netlink.h b/include/uapi/linux/nbd-netlink.h index c2209c75626c..6f7ca3d63a65 100644 --- a/include/uapi/linux/nbd-netlink.h +++ b/include/uapi/linux/nbd-netlink.h | |||
@@ -33,11 +33,35 @@ enum { | |||
33 | NBD_ATTR_CLIENT_FLAGS, | 33 | NBD_ATTR_CLIENT_FLAGS, |
34 | NBD_ATTR_SOCKETS, | 34 | NBD_ATTR_SOCKETS, |
35 | NBD_ATTR_DEAD_CONN_TIMEOUT, | 35 | NBD_ATTR_DEAD_CONN_TIMEOUT, |
36 | NBD_ATTR_DEVICE_LIST, | ||
36 | __NBD_ATTR_MAX, | 37 | __NBD_ATTR_MAX, |
37 | }; | 38 | }; |
38 | #define NBD_ATTR_MAX (__NBD_ATTR_MAX - 1) | 39 | #define NBD_ATTR_MAX (__NBD_ATTR_MAX - 1) |
39 | 40 | ||
40 | /* | 41 | /* |
42 | * This is the format for multiple devices with NBD_ATTR_DEVICE_LIST | ||
43 | * | ||
44 | * [NBD_ATTR_DEVICE_LIST] | ||
45 | * [NBD_DEVICE_ITEM] | ||
46 | * [NBD_DEVICE_INDEX] | ||
47 | * [NBD_DEVICE_CONNECTED] | ||
48 | */ | ||
49 | enum { | ||
50 | NBD_DEVICE_ITEM_UNSPEC, | ||
51 | NBD_DEVICE_ITEM, | ||
52 | __NBD_DEVICE_ITEM_MAX, | ||
53 | }; | ||
54 | #define NBD_DEVICE_ITEM_MAX (__NBD_DEVICE_ITEM_MAX - 1) | ||
55 | |||
56 | enum { | ||
57 | NBD_DEVICE_UNSPEC, | ||
58 | NBD_DEVICE_INDEX, | ||
59 | NBD_DEVICE_CONNECTED, | ||
60 | __NBD_DEVICE_MAX, | ||
61 | }; | ||
62 | #define NBD_DEVICE_ATTR_MAX (__NBD_DEVICE_MAX - 1) | ||
63 | |||
64 | /* | ||
41 | * This is the format for multiple sockets with NBD_ATTR_SOCKETS | 65 | * This is the format for multiple sockets with NBD_ATTR_SOCKETS |
42 | * | 66 | * |
43 | * [NBD_ATTR_SOCKETS] | 67 | * [NBD_ATTR_SOCKETS] |
@@ -66,6 +90,7 @@ enum { | |||
66 | NBD_CMD_DISCONNECT, | 90 | NBD_CMD_DISCONNECT, |
67 | NBD_CMD_RECONFIGURE, | 91 | NBD_CMD_RECONFIGURE, |
68 | NBD_CMD_LINK_DEAD, | 92 | NBD_CMD_LINK_DEAD, |
93 | NBD_CMD_STATUS, | ||
69 | __NBD_CMD_MAX, | 94 | __NBD_CMD_MAX, |
70 | }; | 95 | }; |
71 | #define NBD_CMD_MAX (__NBD_CMD_MAX - 1) | 96 | #define NBD_CMD_MAX (__NBD_CMD_MAX - 1) |