diff options
Diffstat (limited to 'drivers/block/nbd.c')
| -rw-r--r-- | drivers/block/nbd.c | 42 |
1 files changed, 34 insertions, 8 deletions
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 3b7083b8ecbb..74a05561b620 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c | |||
| @@ -76,6 +76,7 @@ struct link_dead_args { | |||
| 76 | #define NBD_HAS_CONFIG_REF 4 | 76 | #define NBD_HAS_CONFIG_REF 4 |
| 77 | #define NBD_BOUND 5 | 77 | #define NBD_BOUND 5 |
| 78 | #define NBD_DESTROY_ON_DISCONNECT 6 | 78 | #define NBD_DESTROY_ON_DISCONNECT 6 |
| 79 | #define NBD_DISCONNECT_ON_CLOSE 7 | ||
| 79 | 80 | ||
| 80 | struct nbd_config { | 81 | struct nbd_config { |
| 81 | u32 flags; | 82 | u32 flags; |
| @@ -138,6 +139,7 @@ static void nbd_config_put(struct nbd_device *nbd); | |||
| 138 | static void nbd_connect_reply(struct genl_info *info, int index); | 139 | static void nbd_connect_reply(struct genl_info *info, int index); |
| 139 | static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info); | 140 | static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info); |
| 140 | static void nbd_dead_link_work(struct work_struct *work); | 141 | static void nbd_dead_link_work(struct work_struct *work); |
| 142 | static void nbd_disconnect_and_put(struct nbd_device *nbd); | ||
| 141 | 143 | ||
| 142 | static inline struct device *nbd_to_dev(struct nbd_device *nbd) | 144 | static inline struct device *nbd_to_dev(struct nbd_device *nbd) |
| 143 | { | 145 | { |
| @@ -1305,6 +1307,12 @@ out: | |||
| 1305 | static void nbd_release(struct gendisk *disk, fmode_t mode) | 1307 | static void nbd_release(struct gendisk *disk, fmode_t mode) |
| 1306 | { | 1308 | { |
| 1307 | struct nbd_device *nbd = disk->private_data; | 1309 | struct nbd_device *nbd = disk->private_data; |
| 1310 | struct block_device *bdev = bdget_disk(disk, 0); | ||
| 1311 | |||
| 1312 | if (test_bit(NBD_DISCONNECT_ON_CLOSE, &nbd->config->runtime_flags) && | ||
| 1313 | bdev->bd_openers == 0) | ||
| 1314 | nbd_disconnect_and_put(nbd); | ||
| 1315 | |||
| 1308 | nbd_config_put(nbd); | 1316 | nbd_config_put(nbd); |
| 1309 | nbd_put(nbd); | 1317 | nbd_put(nbd); |
| 1310 | } | 1318 | } |
| @@ -1705,6 +1713,10 @@ again: | |||
| 1705 | &config->runtime_flags); | 1713 | &config->runtime_flags); |
| 1706 | put_dev = true; | 1714 | put_dev = true; |
| 1707 | } | 1715 | } |
| 1716 | if (flags & NBD_CFLAG_DISCONNECT_ON_CLOSE) { | ||
| 1717 | set_bit(NBD_DISCONNECT_ON_CLOSE, | ||
| 1718 | &config->runtime_flags); | ||
| 1719 | } | ||
| 1708 | } | 1720 | } |
| 1709 | 1721 | ||
| 1710 | if (info->attrs[NBD_ATTR_SOCKETS]) { | 1722 | if (info->attrs[NBD_ATTR_SOCKETS]) { |
| @@ -1749,6 +1761,17 @@ out: | |||
| 1749 | return ret; | 1761 | return ret; |
| 1750 | } | 1762 | } |
| 1751 | 1763 | ||
| 1764 | static void nbd_disconnect_and_put(struct nbd_device *nbd) | ||
| 1765 | { | ||
| 1766 | mutex_lock(&nbd->config_lock); | ||
| 1767 | nbd_disconnect(nbd); | ||
| 1768 | nbd_clear_sock(nbd); | ||
| 1769 | mutex_unlock(&nbd->config_lock); | ||
| 1770 | if (test_and_clear_bit(NBD_HAS_CONFIG_REF, | ||
| 1771 | &nbd->config->runtime_flags)) | ||
| 1772 | nbd_config_put(nbd); | ||
| 1773 | } | ||
| 1774 | |||
| 1752 | static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) | 1775 | static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) |
| 1753 | { | 1776 | { |
| 1754 | struct nbd_device *nbd; | 1777 | struct nbd_device *nbd; |
| @@ -1781,13 +1804,7 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) | |||
| 1781 | nbd_put(nbd); | 1804 | nbd_put(nbd); |
| 1782 | return 0; | 1805 | return 0; |
| 1783 | } | 1806 | } |
| 1784 | mutex_lock(&nbd->config_lock); | 1807 | nbd_disconnect_and_put(nbd); |
| 1785 | nbd_disconnect(nbd); | ||
| 1786 | nbd_clear_sock(nbd); | ||
| 1787 | mutex_unlock(&nbd->config_lock); | ||
| 1788 | if (test_and_clear_bit(NBD_HAS_CONFIG_REF, | ||
| 1789 | &nbd->config->runtime_flags)) | ||
| 1790 | nbd_config_put(nbd); | ||
| 1791 | nbd_config_put(nbd); | 1808 | nbd_config_put(nbd); |
| 1792 | nbd_put(nbd); | 1809 | nbd_put(nbd); |
| 1793 | return 0; | 1810 | return 0; |
| @@ -1798,7 +1815,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | |||
| 1798 | struct nbd_device *nbd = NULL; | 1815 | struct nbd_device *nbd = NULL; |
| 1799 | struct nbd_config *config; | 1816 | struct nbd_config *config; |
| 1800 | int index; | 1817 | int index; |
| 1801 | int ret = -EINVAL; | 1818 | int ret = 0; |
| 1802 | bool put_dev = false; | 1819 | bool put_dev = false; |
| 1803 | 1820 | ||
| 1804 | if (!netlink_capable(skb, CAP_SYS_ADMIN)) | 1821 | if (!netlink_capable(skb, CAP_SYS_ADMIN)) |
| @@ -1838,6 +1855,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | |||
| 1838 | !nbd->task_recv) { | 1855 | !nbd->task_recv) { |
| 1839 | dev_err(nbd_to_dev(nbd), | 1856 | dev_err(nbd_to_dev(nbd), |
| 1840 | "not configured, cannot reconfigure\n"); | 1857 | "not configured, cannot reconfigure\n"); |
| 1858 | ret = -EINVAL; | ||
| 1841 | goto out; | 1859 | goto out; |
| 1842 | } | 1860 | } |
| 1843 | 1861 | ||
| @@ -1862,6 +1880,14 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) | |||
| 1862 | &config->runtime_flags)) | 1880 | &config->runtime_flags)) |
| 1863 | refcount_inc(&nbd->refs); | 1881 | refcount_inc(&nbd->refs); |
| 1864 | } | 1882 | } |
| 1883 | |||
| 1884 | if (flags & NBD_CFLAG_DISCONNECT_ON_CLOSE) { | ||
| 1885 | set_bit(NBD_DISCONNECT_ON_CLOSE, | ||
| 1886 | &config->runtime_flags); | ||
| 1887 | } else { | ||
| 1888 | clear_bit(NBD_DISCONNECT_ON_CLOSE, | ||
| 1889 | &config->runtime_flags); | ||
| 1890 | } | ||
| 1865 | } | 1891 | } |
| 1866 | 1892 | ||
| 1867 | if (info->attrs[NBD_ATTR_SOCKETS]) { | 1893 | if (info->attrs[NBD_ATTR_SOCKETS]) { |
