Skip to content

Commit f31fe81

Browse files
Naman Jaingregkh
authored andcommitted
uio_hv_generic: Fix sysfs creation path for ring buffer
On regular bootup, devices get registered to VMBus first, so when uio_hv_generic driver for a particular device type is probed, the device is already initialized and added, so sysfs creation in hv_uio_probe() works fine. However, when the device is removed and brought back, the channel gets rescinded and the device again gets registered to VMBus. However this time, the uio_hv_generic driver is already registered to probe for that device and in this case sysfs creation is tried before the device's kobject gets initialized completely. Fix this by moving the core logic of sysfs creation of ring buffer, from uio_hv_generic to HyperV's VMBus driver, where the rest of the sysfs attributes for the channels are defined. While doing that, make use of attribute groups and macros, instead of creating sysfs directly, to ensure better error handling and code flow. Problematic path: vmbus_process_offer (A new offer comes for the VMBus device) vmbus_add_channel_work vmbus_device_register |-> device_register | |... | |-> hv_uio_probe | |... | |-> sysfs_create_bin_file (leads to a warning as | the primary channel's kobject, which is used to | create the sysfs file, is not yet initialized) |-> kset_create_and_add |-> vmbus_add_channel_kobj (initialization of the primary channel's kobject happens later) Above code flow is sequential and the warning is always reproducible in this path. Fixes: 9ab877a ("uio_hv_generic: make ring buffer attribute for primary channel") Cc: stable@kernel.org Suggested-by: Saurabh Sengar <ssengar@linux.microsoft.com> Suggested-by: Michael Kelley <mhklinux@outlook.com> Reviewed-by: Michael Kelley <mhklinux@outlook.com> Tested-by: Michael Kelley <mhklinux@outlook.com> Reviewed-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: Naman Jain <namjain@linux.microsoft.com> Link: https://lore.kernel.org/r/20250502074811.2022-2-namjain@linux.microsoft.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent f55aaec commit f31fe81

File tree

4 files changed

+128
-23
lines changed

4 files changed

+128
-23
lines changed

drivers/hv/hyperv_vmbus.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,10 @@ static inline int hv_debug_add_dev_dir(struct hv_device *dev)
477477

478478
#endif /* CONFIG_HYPERV_TESTING */
479479

480+
/* Create and remove sysfs entry for memory mapped ring buffers for a channel */
481+
int hv_create_ring_sysfs(struct vmbus_channel *channel,
482+
int (*hv_mmap_ring_buffer)(struct vmbus_channel *channel,
483+
struct vm_area_struct *vma));
484+
int hv_remove_ring_sysfs(struct vmbus_channel *channel);
485+
480486
#endif /* _HYPERV_VMBUS_H */

drivers/hv/vmbus_drv.c

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,27 @@ static ssize_t subchannel_id_show(struct vmbus_channel *channel,
18021802
}
18031803
static VMBUS_CHAN_ATTR_RO(subchannel_id);
18041804

1805+
static int hv_mmap_ring_buffer_wrapper(struct file *filp, struct kobject *kobj,
1806+
const struct bin_attribute *attr,
1807+
struct vm_area_struct *vma)
1808+
{
1809+
struct vmbus_channel *channel = container_of(kobj, struct vmbus_channel, kobj);
1810+
1811+
/*
1812+
* hv_(create|remove)_ring_sysfs implementation ensures that mmap_ring_buffer
1813+
* is not NULL.
1814+
*/
1815+
return channel->mmap_ring_buffer(channel, vma);
1816+
}
1817+
1818+
static struct bin_attribute chan_attr_ring_buffer = {
1819+
.attr = {
1820+
.name = "ring",
1821+
.mode = 0600,
1822+
},
1823+
.size = 2 * SZ_2M,
1824+
.mmap = hv_mmap_ring_buffer_wrapper,
1825+
};
18051826
static struct attribute *vmbus_chan_attrs[] = {
18061827
&chan_attr_out_mask.attr,
18071828
&chan_attr_in_mask.attr,
@@ -1821,6 +1842,11 @@ static struct attribute *vmbus_chan_attrs[] = {
18211842
NULL
18221843
};
18231844

1845+
static struct bin_attribute *vmbus_chan_bin_attrs[] = {
1846+
&chan_attr_ring_buffer,
1847+
NULL
1848+
};
1849+
18241850
/*
18251851
* Channel-level attribute_group callback function. Returns the permission for
18261852
* each attribute, and returns 0 if an attribute is not visible.
@@ -1841,16 +1867,88 @@ static umode_t vmbus_chan_attr_is_visible(struct kobject *kobj,
18411867
return attr->mode;
18421868
}
18431869

1870+
static umode_t vmbus_chan_bin_attr_is_visible(struct kobject *kobj,
1871+
const struct bin_attribute *attr, int idx)
1872+
{
1873+
const struct vmbus_channel *channel =
1874+
container_of(kobj, struct vmbus_channel, kobj);
1875+
1876+
/* Hide ring attribute if channel's ring_sysfs_visible is set to false */
1877+
if (attr == &chan_attr_ring_buffer && !channel->ring_sysfs_visible)
1878+
return 0;
1879+
1880+
return attr->attr.mode;
1881+
}
1882+
18441883
static const struct attribute_group vmbus_chan_group = {
18451884
.attrs = vmbus_chan_attrs,
1846-
.is_visible = vmbus_chan_attr_is_visible
1885+
.bin_attrs = vmbus_chan_bin_attrs,
1886+
.is_visible = vmbus_chan_attr_is_visible,
1887+
.is_bin_visible = vmbus_chan_bin_attr_is_visible,
18471888
};
18481889

18491890
static const struct kobj_type vmbus_chan_ktype = {
18501891
.sysfs_ops = &vmbus_chan_sysfs_ops,
18511892
.release = vmbus_chan_release,
18521893
};
18531894

1895+
/**
1896+
* hv_create_ring_sysfs() - create "ring" sysfs entry corresponding to ring buffers for a channel.
1897+
* @channel: Pointer to vmbus_channel structure
1898+
* @hv_mmap_ring_buffer: function pointer for initializing the function to be called on mmap of
1899+
* channel's "ring" sysfs node, which is for the ring buffer of that channel.
1900+
* Function pointer is of below type:
1901+
* int (*hv_mmap_ring_buffer)(struct vmbus_channel *channel,
1902+
* struct vm_area_struct *vma))
1903+
* This has a pointer to the channel and a pointer to vm_area_struct,
1904+
* used for mmap, as arguments.
1905+
*
1906+
* Sysfs node for ring buffer of a channel is created along with other fields, however its
1907+
* visibility is disabled by default. Sysfs creation needs to be controlled when the use-case
1908+
* is running.
1909+
* For example, HV_NIC device is used either by uio_hv_generic or hv_netvsc at any given point of
1910+
* time, and "ring" sysfs is needed only when uio_hv_generic is bound to that device. To avoid
1911+
* exposing the ring buffer by default, this function is reponsible to enable visibility of
1912+
* ring for userspace to use.
1913+
* Note: Race conditions can happen with userspace and it is not encouraged to create new
1914+
* use-cases for this. This was added to maintain backward compatibility, while solving
1915+
* one of the race conditions in uio_hv_generic while creating sysfs.
1916+
*
1917+
* Returns 0 on success or error code on failure.
1918+
*/
1919+
int hv_create_ring_sysfs(struct vmbus_channel *channel,
1920+
int (*hv_mmap_ring_buffer)(struct vmbus_channel *channel,
1921+
struct vm_area_struct *vma))
1922+
{
1923+
struct kobject *kobj = &channel->kobj;
1924+
1925+
channel->mmap_ring_buffer = hv_mmap_ring_buffer;
1926+
channel->ring_sysfs_visible = true;
1927+
1928+
return sysfs_update_group(kobj, &vmbus_chan_group);
1929+
}
1930+
EXPORT_SYMBOL_GPL(hv_create_ring_sysfs);
1931+
1932+
/**
1933+
* hv_remove_ring_sysfs() - remove ring sysfs entry corresponding to ring buffers for a channel.
1934+
* @channel: Pointer to vmbus_channel structure
1935+
*
1936+
* Hide "ring" sysfs for a channel by changing its is_visible attribute and updating sysfs group.
1937+
*
1938+
* Returns 0 on success or error code on failure.
1939+
*/
1940+
int hv_remove_ring_sysfs(struct vmbus_channel *channel)
1941+
{
1942+
struct kobject *kobj = &channel->kobj;
1943+
int ret;
1944+
1945+
channel->ring_sysfs_visible = false;
1946+
ret = sysfs_update_group(kobj, &vmbus_chan_group);
1947+
channel->mmap_ring_buffer = NULL;
1948+
return ret;
1949+
}
1950+
EXPORT_SYMBOL_GPL(hv_remove_ring_sysfs);
1951+
18541952
/*
18551953
* vmbus_add_channel_kobj - setup a sub-directory under device/channels
18561954
*/

drivers/uio/uio_hv_generic.c

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,12 @@ static void hv_uio_rescind(struct vmbus_channel *channel)
131131
vmbus_device_unregister(channel->device_obj);
132132
}
133133

134-
/* Sysfs API to allow mmap of the ring buffers
134+
/* Function used for mmap of ring buffer sysfs interface.
135135
* The ring buffer is allocated as contiguous memory by vmbus_open
136136
*/
137-
static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
138-
const struct bin_attribute *attr,
139-
struct vm_area_struct *vma)
137+
static int
138+
hv_uio_ring_mmap(struct vmbus_channel *channel, struct vm_area_struct *vma)
140139
{
141-
struct vmbus_channel *channel
142-
= container_of(kobj, struct vmbus_channel, kobj);
143140
void *ring_buffer = page_address(channel->ringbuffer_page);
144141

145142
if (channel->state != CHANNEL_OPENED_STATE)
@@ -149,15 +146,6 @@ static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
149146
channel->ringbuffer_pagecount << PAGE_SHIFT);
150147
}
151148

152-
static const struct bin_attribute ring_buffer_bin_attr = {
153-
.attr = {
154-
.name = "ring",
155-
.mode = 0600,
156-
},
157-
.size = 2 * SZ_2M,
158-
.mmap = hv_uio_ring_mmap,
159-
};
160-
161149
/* Callback from VMBUS subsystem when new channel created. */
162150
static void
163151
hv_uio_new_channel(struct vmbus_channel *new_sc)
@@ -178,8 +166,7 @@ hv_uio_new_channel(struct vmbus_channel *new_sc)
178166
/* Disable interrupts on sub channel */
179167
new_sc->inbound.ring_buffer->interrupt_mask = 1;
180168
set_channel_read_mode(new_sc, HV_CALL_ISR);
181-
182-
ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr);
169+
ret = hv_create_ring_sysfs(new_sc, hv_uio_ring_mmap);
183170
if (ret) {
184171
dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
185172
vmbus_close(new_sc);
@@ -350,10 +337,18 @@ hv_uio_probe(struct hv_device *dev,
350337
goto fail_close;
351338
}
352339

353-
ret = sysfs_create_bin_file(&channel->kobj, &ring_buffer_bin_attr);
354-
if (ret)
355-
dev_notice(&dev->device,
356-
"sysfs create ring bin file failed; %d\n", ret);
340+
/*
341+
* This internally calls sysfs_update_group, which returns a non-zero value if it executes
342+
* before sysfs_create_group. This is expected as the 'ring' will be created later in
343+
* vmbus_device_register() -> vmbus_add_channel_kobj(). Thus, no need to check the return
344+
* value and print warning.
345+
*
346+
* Creating/exposing sysfs in driver probe is not encouraged as it can lead to race
347+
* conditions with userspace. For backward compatibility, "ring" sysfs could not be removed
348+
* or decoupled from uio_hv_generic probe. Userspace programs can make use of inotify
349+
* APIs to make sure that ring is created.
350+
*/
351+
hv_create_ring_sysfs(channel, hv_uio_ring_mmap);
357352

358353
hv_set_drvdata(dev, pdata);
359354

@@ -375,7 +370,7 @@ hv_uio_remove(struct hv_device *dev)
375370
if (!pdata)
376371
return;
377372

378-
sysfs_remove_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr);
373+
hv_remove_ring_sysfs(dev->channel);
379374
uio_unregister_device(&pdata->info);
380375
hv_uio_cleanup(dev, pdata);
381376

include/linux/hyperv.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,12 @@ struct vmbus_channel {
10021002

10031003
/* The max size of a packet on this channel */
10041004
u32 max_pkt_size;
1005+
1006+
/* function to mmap ring buffer memory to the channel's sysfs ring attribute */
1007+
int (*mmap_ring_buffer)(struct vmbus_channel *channel, struct vm_area_struct *vma);
1008+
1009+
/* boolean to control visibility of sysfs for ring buffer */
1010+
bool ring_sysfs_visible;
10051011
};
10061012

10071013
#define lock_requestor(channel, flags) \

0 commit comments

Comments
 (0)