[PATCH] Drivers: hv: Allow single instance of hv_util device

Sonia Sharma posted 1 patch 17 hours ago
drivers/hv/channel_mgmt.c | 64 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
[PATCH] Drivers: hv: Allow single instance of hv_util device
Posted by Sonia Sharma 17 hours ago
From: Sonia Sharma <sonia.sharma@linux.microsoft.com>

Harden hv_util type device drivers to allow single
instance of the device be configured at given time.

New function vmbus_is_valid_hvutil_offer() is added.
It checks if the new offer is for hv_util device type,
then read the refcount for that device and accept or
reject the offer accordingly.

Signed-off-by: Sonia Sharma <sonia.sharma@linux.microsoft.com>
---
 drivers/hv/channel_mgmt.c | 64 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 3c6011a48dab..1a135cfad81f 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -20,6 +20,7 @@
 #include <linux/delay.h>
 #include <linux/cpu.h>
 #include <linux/hyperv.h>
+#include <linux/refcount.h>
 #include <asm/mshyperv.h>
 #include <linux/sched/isolation.h>
 
@@ -156,6 +157,8 @@ const struct vmbus_device vmbus_devs[] = {
 };
 EXPORT_SYMBOL_GPL(vmbus_devs);
 
+static refcount_t singleton_vmbus_devs[HV_UNKNOWN + 1];
+
 static const struct {
 	guid_t guid;
 } vmbus_unsupported_devs[] = {
@@ -198,6 +201,25 @@ static bool is_unsupported_vmbus_devs(const guid_t *guid)
 	return false;
 }
 
+static bool is_dev_hv_util(u16 dev_type)
+{
+	switch (dev_type) {
+	case HV_SHUTDOWN:
+		fallthrough;
+	case HV_TS:
+		fallthrough;
+	case HV_HB:
+		fallthrough;
+	case HV_KVP:
+		fallthrough;
+	case HV_BACKUP:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
 static u16 hv_get_dev_type(const struct vmbus_channel *channel)
 {
 	const guid_t *guid = &channel->offermsg.offer.if_type;
@@ -1004,6 +1026,26 @@ find_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer)
 	return channel;
 }
 
+static u16 vmbus_is_valid_hvutil_offer(const struct vmbus_channel_offer_channel *offer)
+{
+	const guid_t *guid = &offer->offer.if_type;
+	u16 i;
+
+	if (is_hvsock_offer(offer))
+		return HV_UNKNOWN;
+
+	for (i = HV_IDE; i < HV_UNKNOWN; i++) {
+		if (guid_equal(guid, &vmbus_devs[i].guid) && is_dev_hv_util(i)) {
+			if (refcount_read(&singleton_vmbus_devs[i]))
+				return HV_UNKNOWN;
+			refcount_set(&singleton_vmbus_devs[i], 1);
+			return i;
+		}
+	}
+
+	return i;
+}
+
 static bool vmbus_is_valid_offer(const struct vmbus_channel_offer_channel *offer)
 {
 	const guid_t *guid = &offer->offer.if_type;
@@ -1031,6 +1073,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
 	struct vmbus_channel_offer_channel *offer;
 	struct vmbus_channel *oldchannel, *newchannel;
 	size_t offer_sz;
+	u16 dev_type;
 
 	offer = (struct vmbus_channel_offer_channel *)hdr;
 
@@ -1115,11 +1158,29 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
 		return;
 	}
 
+	/*
+	 * If vmbus_is_valid_offer() returns -
+	 *	HV_UNKNOWN - Subsequent offer received for hv_util dev, thus reject offer.
+	 *	HV_SHUTDOWN|HV_TS|HV_KVP|HV_HB|HV-KVP|HV_BACKUP - Increment refcount
+	 *	Others - Continue as is without doing anything.
+	 *
+	 * NOTE - We do not want to increase refcount if we resume from hibernation.
+	 */
+	dev_type = vmbus_is_valid_hvutil_offer(offer);
+	if (dev_type == HV_UNKNOWN) {
+		pr_err_ratelimited("Invalid hv_util offer %d from the host supporting "
+			"isolation\n", offer->child_relid);
+		atomic_dec(&vmbus_connection.offer_in_progress);
+		return;
+	}
+
 	/* Allocate the channel object and save this offer. */
 	newchannel = alloc_channel();
 	if (!newchannel) {
 		vmbus_release_relid(offer->child_relid);
 		atomic_dec(&vmbus_connection.offer_in_progress);
+		if (is_dev_hv_util(dev_type))
+			refcount_dec(&singleton_vmbus_devs[dev_type]);
 		pr_err("Unable to allocate channel object\n");
 		return;
 	}
@@ -1235,7 +1296,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
 	/*
 	 * At this point, the rescind handling can proceed safely.
 	 */
-
 	if (channel->device_obj) {
 		if (channel->chn_rescind_callback) {
 			channel->chn_rescind_callback(channel);
@@ -1251,6 +1311,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
 		 */
 		dev = get_device(&channel->device_obj->device);
 		if (dev) {
+			if (is_dev_hv_util(hv_get_dev_type(channel)))
+				refcount_dec(&singleton_vmbus_devs[hv_get_dev_type(channel)]);
 			vmbus_device_unregister(channel->device_obj);
 			put_device(dev);
 		}
-- 
2.34.1