diff mbox series

[2/2] virtio-rng: add sysfs entries for leak detection

Message ID 20230119184349.74072-3-bchalios@amazon.es (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show
Series virtio-rng entropy leak reporting feature | expand

Commit Message

Babis Chalios Jan. 19, 2023, 6:43 p.m. UTC
Make use of the copy-on-leak functionality of the virtio rng driver to
expose a mechanism to user space for detecting entropy leak events, such
as taking a VM snapshot or restoring from one.

The driver setups a single page of memory where it stores in the first
word a counter and queues a copy-on-leak command for increasing the
counter every time an entropy leak occurs. It exposes the value of the
counter in a binary sysfs file per device. The file can be mmap'ed and
read and every time a change on the counter is observed, `sysfs_notify`
is used to notify processes that are polling it.

The mechanism is implemented based on the idea of a VM generation
counter that had been before proposed as an extension to the VM
Generation ID device, where mmap and poll interfaces can be used on the
file containing the counter and changes in its value signal snapshot
events.

It is worth noting that using mmap is entirely race-free, since changes
in the counter are observable by user-space as soon as vcpus are
resumed. Instead, using poll is not race-free. There is a race-window
between the moment the vcpus are resumed and the used-buffers are
handled by the virtio-rng driver.

Signed-off-by: Babis Chalios <bchalios@amazon.es>
---
 drivers/char/hw_random/virtio-rng.c | 166 +++++++++++++++++++++++++++-
 1 file changed, 163 insertions(+), 3 deletions(-)

Comments

kernel test robot Jan. 19, 2023, 10:31 p.m. UTC | #1
Hi Babis,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on char-misc/char-misc-next char-misc/char-misc-linus linus/master v6.2-rc4 next-20230119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
patch link:    https://lore.kernel.org/r/20230119184349.74072-3-bchalios%40amazon.es
patch subject: [PATCH 2/2] virtio-rng: add sysfs entries for leak detection
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301200640.CsblwTsa-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/2a801d93b8225555e4cb293a173e2053870cb2d1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
        git checkout 2a801d93b8225555e4cb293a173e2053870cb2d1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/char/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/char/hw_random/virtio-rng.c:61:9: warning: no previous prototype for 'virtrng_sysfs_read' [-Wmissing-prototypes]
      61 | ssize_t virtrng_sysfs_read(struct file *filep, struct kobject *kobj,
         |         ^~~~~~~~~~~~~~~~~~
>> drivers/char/hw_random/virtio-rng.c:76:5: warning: no previous prototype for 'virtrng_sysfs_mmap' [-Wmissing-prototypes]
      76 | int virtrng_sysfs_mmap(struct file *filep, struct kobject *kobj,
         |     ^~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:106:5: warning: no previous prototype for 'add_fill_on_leak_request' [-Wmissing-prototypes]
     106 | int add_fill_on_leak_request(struct virtrng_info *vi, struct virtqueue *vq, void *data, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:120:5: warning: no previous prototype for 'virtrng_fill_on_leak' [-Wmissing-prototypes]
     120 | int virtrng_fill_on_leak(struct virtrng_info *vi, void *data, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:141:5: warning: no previous prototype for 'add_copy_on_leak_request' [-Wmissing-prototypes]
     141 | int add_copy_on_leak_request(struct virtrng_info *vi, struct virtqueue *vq,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:160:5: warning: no previous prototype for 'virtrng_copy_on_leak' [-Wmissing-prototypes]
     160 | int virtrng_copy_on_leak(struct virtrng_info *vi, void *to, void *from, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~


vim +/virtrng_sysfs_read +61 drivers/char/hw_random/virtio-rng.c

    59	
    60	#ifdef CONFIG_SYSFS
  > 61	ssize_t virtrng_sysfs_read(struct file *filep, struct kobject *kobj,
    62			struct bin_attribute *attr, char *buf, loff_t pos, size_t len)
    63	{
    64		struct virtrng_info *vi = attr->private;
    65		unsigned long gen_counter = *(unsigned long *)vi->map_buffer;
    66	
    67		if (!len)
    68			return 0;
    69	
    70		len = min(len, sizeof(gen_counter));
    71		memcpy(buf, &gen_counter, len);
    72	
    73		return len;
    74	}
    75	
  > 76	int virtrng_sysfs_mmap(struct file *filep, struct kobject *kobj,
    77			struct bin_attribute *attr, struct vm_area_struct *vma)
    78	{
    79		struct virtrng_info *vi = attr->private;
    80	
    81		if (vma->vm_pgoff || vma_pages(vma) > 1)
    82			return -EINVAL;
    83	
    84		if (vma->vm_flags & VM_WRITE)
    85			return -EPERM;
    86	
    87		vma->vm_flags |= VM_DONTEXPAND;
    88		vma->vm_flags &= ~VM_MAYWRITE;
    89	
    90		return vm_insert_page(vma, vma->vm_start, virt_to_page(vi->map_buffer));
    91	}
    92	#endif
    93
kernel test robot Jan. 19, 2023, 10:51 p.m. UTC | #2
Hi Babis,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-testing]
[also build test ERROR on char-misc/char-misc-next char-misc/char-misc-linus linus/master v6.2-rc4 next-20230119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
patch link:    https://lore.kernel.org/r/20230119184349.74072-3-bchalios%40amazon.es
patch subject: [PATCH 2/2] virtio-rng: add sysfs entries for leak detection
config: ia64-randconfig-r023-20230119 (https://download.01.org/0day-ci/archive/20230120/202301200622.6x78gCS0-lkp@intel.com/config)
compiler: ia64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/2a801d93b8225555e4cb293a173e2053870cb2d1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
        git checkout 2a801d93b8225555e4cb293a173e2053870cb2d1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash drivers/char/hw_random/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/char/hw_random/virtio-rng.c:61:9: warning: no previous prototype for 'virtrng_sysfs_read' [-Wmissing-prototypes]
      61 | ssize_t virtrng_sysfs_read(struct file *filep, struct kobject *kobj,
         |         ^~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:76:5: warning: no previous prototype for 'virtrng_sysfs_mmap' [-Wmissing-prototypes]
      76 | int virtrng_sysfs_mmap(struct file *filep, struct kobject *kobj,
         |     ^~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:106:5: warning: no previous prototype for 'add_fill_on_leak_request' [-Wmissing-prototypes]
     106 | int add_fill_on_leak_request(struct virtrng_info *vi, struct virtqueue *vq, void *data, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:120:5: warning: no previous prototype for 'virtrng_fill_on_leak' [-Wmissing-prototypes]
     120 | int virtrng_fill_on_leak(struct virtrng_info *vi, void *data, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:141:5: warning: no previous prototype for 'add_copy_on_leak_request' [-Wmissing-prototypes]
     141 | int add_copy_on_leak_request(struct virtrng_info *vi, struct virtqueue *vq,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:160:5: warning: no previous prototype for 'virtrng_copy_on_leak' [-Wmissing-prototypes]
     160 | int virtrng_copy_on_leak(struct virtrng_info *vi, void *to, void *from, size_t len)
         |     ^~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/device/driver.h:21,
                    from include/linux/device.h:32,
                    from include/linux/virtio.h:9,
                    from drivers/char/hw_random/virtio-rng.c:15:
   include/linux/module.h:130:49: error: redefinition of '__inittest'
     130 |         static inline initcall_t __maybe_unused __inittest(void)                \
         |                                                 ^~~~~~~~~~
   include/linux/device/driver.h:267:1: note: in expansion of macro 'module_init'
     267 | module_init(__driver##_init); \
         | ^~~~~~~~~~~
   include/linux/virtio.h:207:9: note: in expansion of macro 'module_driver'
     207 |         module_driver(__virtio_driver, register_virtio_driver, \
         |         ^~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:609:1: note: in expansion of macro 'module_virtio_driver'
     609 | module_virtio_driver(virtio_rng_driver);
         | ^~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:130:49: note: previous definition of '__inittest' with type 'int (*(void))(void)'
     130 |         static inline initcall_t __maybe_unused __inittest(void)                \
         |                                                 ^~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:605:1: note: in expansion of macro 'module_init'
     605 | module_init(virtio_rng_init);
         | ^~~~~~~~~~~
   include/linux/module.h:132:13: error: redefinition of 'init_module'
     132 |         int init_module(void) __copy(initfn)                    \
         |             ^~~~~~~~~~~
   include/linux/device/driver.h:267:1: note: in expansion of macro 'module_init'
     267 | module_init(__driver##_init); \
         | ^~~~~~~~~~~
   include/linux/virtio.h:207:9: note: in expansion of macro 'module_driver'
     207 |         module_driver(__virtio_driver, register_virtio_driver, \
         |         ^~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:609:1: note: in expansion of macro 'module_virtio_driver'
     609 | module_virtio_driver(virtio_rng_driver);
         | ^~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:132:13: note: previous definition of 'init_module' with type 'int(void)'
     132 |         int init_module(void) __copy(initfn)                    \
         |             ^~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:605:1: note: in expansion of macro 'module_init'
     605 | module_init(virtio_rng_init);
         | ^~~~~~~~~~~
>> include/linux/module.h:138:49: error: redefinition of '__exittest'
     138 |         static inline exitcall_t __maybe_unused __exittest(void)                \
         |                                                 ^~~~~~~~~~
   include/linux/device/driver.h:272:1: note: in expansion of macro 'module_exit'
     272 | module_exit(__driver##_exit);
         | ^~~~~~~~~~~
   include/linux/virtio.h:207:9: note: in expansion of macro 'module_driver'
     207 |         module_driver(__virtio_driver, register_virtio_driver, \
         |         ^~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:609:1: note: in expansion of macro 'module_virtio_driver'
     609 | module_virtio_driver(virtio_rng_driver);
         | ^~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:138:49: note: previous definition of '__exittest' with type 'void (*(void))(void)'
     138 |         static inline exitcall_t __maybe_unused __exittest(void)                \
         |                                                 ^~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:606:1: note: in expansion of macro 'module_exit'
     606 | module_exit(virtio_rng_fini);
         | ^~~~~~~~~~~
>> include/linux/module.h:140:14: error: redefinition of 'cleanup_module'
     140 |         void cleanup_module(void) __copy(exitfn)                \
         |              ^~~~~~~~~~~~~~
   include/linux/device/driver.h:272:1: note: in expansion of macro 'module_exit'
     272 | module_exit(__driver##_exit);
         | ^~~~~~~~~~~
   include/linux/virtio.h:207:9: note: in expansion of macro 'module_driver'
     207 |         module_driver(__virtio_driver, register_virtio_driver, \
         |         ^~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:609:1: note: in expansion of macro 'module_virtio_driver'
     609 | module_virtio_driver(virtio_rng_driver);
         | ^~~~~~~~~~~~~~~~~~~~
   include/linux/module.h:140:14: note: previous definition of 'cleanup_module' with type 'void(void)'
     140 |         void cleanup_module(void) __copy(exitfn)                \
         |              ^~~~~~~~~~~~~~
   drivers/char/hw_random/virtio-rng.c:606:1: note: in expansion of macro 'module_exit'
     606 | module_exit(virtio_rng_fini);
         | ^~~~~~~~~~~


vim +/__exittest +138 include/linux/module.h

0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  127  
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  128  /* Each module must use one module_init(). */
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  129  #define module_init(initfn)					\
1f318a8bafcfba9 Arnd Bergmann  2017-02-01  130  	static inline initcall_t __maybe_unused __inittest(void)		\
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  131  	{ return initfn; }					\
cf68fffb66d60d9 Sami Tolvanen  2021-04-08  132  	int init_module(void) __copy(initfn)			\
cf68fffb66d60d9 Sami Tolvanen  2021-04-08  133  		__attribute__((alias(#initfn)));		\
92efda8eb15295a Sami Tolvanen  2022-09-08  134  	___ADDRESSABLE(init_module, __initdata);
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  135  
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  136  /* This is only required if you want to be unloadable. */
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  137  #define module_exit(exitfn)					\
1f318a8bafcfba9 Arnd Bergmann  2017-02-01 @138  	static inline exitcall_t __maybe_unused __exittest(void)		\
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  139  	{ return exitfn; }					\
cf68fffb66d60d9 Sami Tolvanen  2021-04-08 @140  	void cleanup_module(void) __copy(exitfn)		\
cf68fffb66d60d9 Sami Tolvanen  2021-04-08  141  		__attribute__((alias(#exitfn)));		\
92efda8eb15295a Sami Tolvanen  2022-09-08  142  	___ADDRESSABLE(cleanup_module, __exitdata);
0fd972a7d91d6e1 Paul Gortmaker 2015-05-01  143
kernel test robot Jan. 20, 2023, 3:07 a.m. UTC | #3
Hi Babis,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on char-misc/char-misc-next char-misc/char-misc-linus linus/master v6.2-rc4 next-20230119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
patch link:    https://lore.kernel.org/r/20230119184349.74072-3-bchalios%40amazon.es
patch subject: [PATCH 2/2] virtio-rng: add sysfs entries for leak detection
config: x86_64-randconfig-s021 (https://download.01.org/0day-ci/archive/20230120/202301201004.z1gRttwb-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.4-39-gce1a6720-dirty
        # https://github.com/intel-lab-lkp/linux/commit/2a801d93b8225555e4cb293a173e2053870cb2d1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Babis-Chalios/virtio-rng-implement-entropy-leak-feature/20230120-024631
        git checkout 2a801d93b8225555e4cb293a173e2053870cb2d1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 olddefconfig
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/char/hw_random/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

sparse warnings: (new ones prefixed by >>)
>> drivers/char/hw_random/virtio-rng.c:61:9: sparse: sparse: symbol 'virtrng_sysfs_read' was not declared. Should it be static?
>> drivers/char/hw_random/virtio-rng.c:76:5: sparse: sparse: symbol 'virtrng_sysfs_mmap' was not declared. Should it be static?
   drivers/char/hw_random/virtio-rng.c:106:5: sparse: sparse: symbol 'add_fill_on_leak_request' was not declared. Should it be static?
   drivers/char/hw_random/virtio-rng.c:120:5: sparse: sparse: symbol 'virtrng_fill_on_leak' was not declared. Should it be static?
   drivers/char/hw_random/virtio-rng.c:141:5: sparse: sparse: symbol 'add_copy_on_leak_request' was not declared. Should it be static?
   drivers/char/hw_random/virtio-rng.c:160:5: sparse: sparse: symbol 'virtrng_copy_on_leak' was not declared. Should it be static?
diff mbox series

Patch

diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 389a091a8801..003320f6c574 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -5,6 +5,9 @@ 
  */
 
 #include "asm-generic/errno.h"
+#include "linux/gfp.h"
+#include "linux/minmax.h"
+#include "linux/sysfs.h"
 #include <linux/err.h>
 #include <linux/hw_random.h>
 #include <linux/scatterlist.h>
@@ -17,6 +20,10 @@ 
 
 static DEFINE_IDA(rng_index_ida);
 
+#ifdef CONFIG_SYSFS
+static struct kobject *virtio_rng_kobj;
+#endif
+
 struct virtrng_info {
 	struct hwrng hwrng;
 	struct virtqueue *vq;
@@ -25,6 +32,12 @@  struct virtrng_info {
 	struct virtqueue *leakq[2];
 	spinlock_t lock;
 	int active_leakq;
+#ifdef CONFIG_SYSFS
+	struct kobject *kobj;
+	struct bin_attribute vm_gen_counter_attr;
+	unsigned long map_buffer;
+	unsigned long next_vm_gen_counter;
+#endif
 
 	char name[25];
 	int index;
@@ -44,6 +57,40 @@  struct virtrng_info {
 #endif
 };
 
+#ifdef CONFIG_SYSFS
+ssize_t virtrng_sysfs_read(struct file *filep, struct kobject *kobj,
+		struct bin_attribute *attr, char *buf, loff_t pos, size_t len)
+{
+	struct virtrng_info *vi = attr->private;
+	unsigned long gen_counter = *(unsigned long *)vi->map_buffer;
+
+	if (!len)
+		return 0;
+
+	len = min(len, sizeof(gen_counter));
+	memcpy(buf, &gen_counter, len);
+
+	return len;
+}
+
+int virtrng_sysfs_mmap(struct file *filep, struct kobject *kobj,
+		struct bin_attribute *attr, struct vm_area_struct *vma)
+{
+	struct virtrng_info *vi = attr->private;
+
+	if (vma->vm_pgoff || vma_pages(vma) > 1)
+		return -EINVAL;
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EPERM;
+
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_flags &= ~VM_MAYWRITE;
+
+	return vm_insert_page(vma, vma->vm_start, virt_to_page(vi->map_buffer));
+}
+#endif
+
 /* Swaps the queues and returns the new active leak queue. */
 static struct virtqueue *swap_leakqs(struct virtrng_info *vi)
 {
@@ -83,7 +130,7 @@  int virtrng_fill_on_leak(struct virtrng_info *vi, void *data, size_t len)
 
 	vq = get_active_leakq(vi);
 	ret = add_fill_on_leak_request(vi, vq, data, len);
-	if (ret)
+	if (!ret)
 		virtqueue_kick(vq);
 
 	spin_unlock_irqrestore(&vi->lock, flags);
@@ -123,7 +170,7 @@  int virtrng_copy_on_leak(struct virtrng_info *vi, void *to, void *from, size_t l
 
 	vq = get_active_leakq(vi);
 	ret = add_copy_on_leak_request(vi, vq, to, from, len);
-	if (ret)
+	if (!ret)
 		virtqueue_kick(vq);
 
 	spin_unlock_irqrestore(&vi->lock, flags);
@@ -139,6 +186,9 @@  static void entropy_leak_detected(struct virtqueue *vq)
 	unsigned long flags;
 	void *buffer;
 	bool kick_activeq = false;
+#ifdef CONFIG_SYSFS
+	bool notify_sysfs = false;
+#endif
 
 	spin_lock_irqsave(&vi->lock, flags);
 
@@ -160,12 +210,34 @@  static void entropy_leak_detected(struct virtqueue *vq)
 			add_fill_on_leak_request(vi, activeq, vi->leak_data, sizeof(vi->leak_data));
 			kick_activeq = true;
 		}
+
+#ifdef CONFIG_SYSFS
+		if (buffer == (void *)vi->map_buffer) {
+			notify_sysfs = true;
+
+			/* Add a request to bump the generation counter on the next leak event.
+			 * We have already swapped leak queues, so this will get properly handled
+			 * with the next entropy leak event.
+			 */
+			vi->next_vm_gen_counter++;
+			add_copy_on_leak_request(vi, activeq, (void *)vi->map_buffer,
+					&vi->next_vm_gen_counter, sizeof(unsigned long));
+
+			kick_activeq = true;
+		}
+#endif
 	}
 
 	if (kick_activeq)
 		virtqueue_kick(activeq);
 
 	spin_unlock_irqrestore(&vi->lock, flags);
+
+#ifdef CONFIG_SYSFS
+	/* Notify anyone polling on the sysfs file */
+	if (notify_sysfs)
+		sysfs_notify(vi->kobj, NULL, "vm_gen_counter");
+#endif
 }
 
 static void random_recv_done(struct virtqueue *vq)
@@ -302,6 +374,59 @@  static int init_virtqueues(struct virtrng_info *vi, struct virtio_device *vdev)
 	return ret;
 }
 
+#ifdef CONFIG_SYSFS
+static int setup_sysfs(struct virtrng_info *vi)
+{
+	int err;
+
+	vi->next_vm_gen_counter = 1;
+
+	/* We have one binary file per device under /sys/virtio-rng/<device>/vm_gen_counter */
+	vi->vm_gen_counter_attr.attr.name = "vm_gen_counter";
+	vi->vm_gen_counter_attr.attr.mode = 0444;
+	vi->vm_gen_counter_attr.read = virtrng_sysfs_read;
+	vi->vm_gen_counter_attr.mmap = virtrng_sysfs_mmap;
+	vi->vm_gen_counter_attr.private = vi;
+
+	vi->map_buffer = get_zeroed_page(GFP_KERNEL);
+	if (!vi->map_buffer)
+		return -ENOMEM;
+
+	err = -ENOMEM;
+	vi->kobj = kobject_create_and_add(vi->name, virtio_rng_kobj);
+	if (!vi->kobj)
+		goto err_page;
+
+	err = sysfs_create_bin_file(vi->kobj, &vi->vm_gen_counter_attr);
+	if (err)
+		goto err_kobj;
+
+	return 0;
+
+err_kobj:
+	kobject_put(vi->kobj);
+err_page:
+	free_pages(vi->map_buffer, 0);
+	return err;
+}
+
+static void cleanup_sysfs(struct virtrng_info *vi)
+{
+	sysfs_remove_bin_file(vi->kobj, &vi->vm_gen_counter_attr);
+	kobject_put(vi->kobj);
+	free_pages(vi->map_buffer, 0);
+}
+#else
+static int setup_sysfs(struct virtrng_info *vi)
+{
+	return 0;
+}
+
+static void cleanup_sysfs(struct virtrng_info *vi)
+{
+}
+#endif
+
 static int probe_common(struct virtio_device *vdev)
 {
 	int err, index;
@@ -332,11 +457,15 @@  static int probe_common(struct virtio_device *vdev)
 	if (vi->has_leakqs) {
 		spin_lock_init(&vi->lock);
 		vi->active_leakq = 0;
+
+		err = setup_sysfs(vi);
+		if (err)
+			goto err_find;
 	}
 
 	err = init_virtqueues(vi, vdev);
 	if (err)
-		goto err_find;
+		goto err_sysfs;
 
 	virtio_device_ready(vdev);
 
@@ -346,8 +475,18 @@  static int probe_common(struct virtio_device *vdev)
 	/* we always have a fill_on_leak request pending */
 	virtrng_fill_on_leak(vi, vi->leak_data, sizeof(vi->leak_data));
 
+#ifdef CONFIG_SYSFS
+	/* also a copy_on_leak request for the generation counter when we have sysfs
+	 * support.
+	 */
+	virtrng_copy_on_leak(vi, (void *)vi->map_buffer, &vi->next_vm_gen_counter,
+			sizeof(unsigned long));
+#endif
+
 	return 0;
 
+err_sysfs:
+	cleanup_sysfs(vi);
 err_find:
 	ida_simple_remove(&rng_index_ida, index);
 err_ida:
@@ -365,6 +504,8 @@  static void remove_common(struct virtio_device *vdev)
 	complete(&vi->have_data);
 	if (vi->hwrng_register_done)
 		hwrng_unregister(&vi->hwrng);
+	if (vi->has_leakqs)
+		cleanup_sysfs(vi);
 	virtio_reset_device(vdev);
 	vdev->config->del_vqs(vdev);
 	ida_simple_remove(&rng_index_ida, vi->index);
@@ -447,6 +588,25 @@  static struct virtio_driver virtio_rng_driver = {
 #endif
 };
 
+#ifdef CONFIG_SYSFS
+static int __init virtio_rng_init(void)
+{
+	virtio_rng_kobj = kobject_create_and_add("virtio-rng", NULL);
+	if (!virtio_rng_kobj)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void __exit virtio_rng_fini(void)
+{
+	kobject_put(virtio_rng_kobj);
+}
+
+module_init(virtio_rng_init);
+module_exit(virtio_rng_fini);
+#endif
+
 module_virtio_driver(virtio_rng_driver);
 MODULE_DEVICE_TABLE(virtio, id_table);
 MODULE_DESCRIPTION("Virtio random number driver");