drivers/hv/channel_mgmt.c | 64 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-)
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
© 2016 - 2024 Red Hat, Inc.