summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathon Fernyhough2021-01-01 16:11:40 +0000
committerJonathon Fernyhough2021-01-01 16:11:40 +0000
commit614fcd8ea53184b4c794fef58297699b058bebef (patch)
tree31b4e401b1fd7d628e7534baeab2d6fdc72e553a
parentb24d23c6f54b7a81363c8543c24a5a2ca2534fc2 (diff)
downloadaur-614fcd8ea53184b4c794fef58297699b058bebef.tar.gz
Fix nvidia-uvm
-rw-r--r--.SRCINFO8
-rw-r--r--PKGBUILD43
-rw-r--r--kernel-5.8.patch8
-rw-r--r--uvm_common-415.18.c378
4 files changed, 401 insertions, 36 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 41e0ec88cdc7..8be720d181d5 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = nvidia-390xx
pkgdesc = NVIDIA drivers for linux, 390xx legacy branch
pkgver = 390.138
- pkgrel = 9
+ pkgrel = 10
url = https://www.nvidia.com/
arch = x86_64
license = custom
@@ -15,13 +15,13 @@ pkgbase = nvidia-390xx
source = kernel-5.8.patch
source = kernel-5.9.patch
source = https://gitlab.com/herecura/packages/nvidia-390xx-dkms/-/raw/db83ed8ac2e0e0097d535a82e2fd4ee0f31873e3/kernel-5.10.patch
- source = https://salsa.debian.org/nvidia-team/nvidia-graphics-drivers/-/raw/4ba8f0d0b7fc3e7c2cce19c23930b279c15280e5/debian/module/debian/patches/disable-module-nvidia-uvm.patch
+ source = uvm_common-415.18.c
b2sums = bf56cef38d76accdf547b96cd444b0bd4288f604d44a1d274f3246c13310d6a59050b36f384280edb83938db81fa0544f7a2dc84ff6f440ff90b76ee62749fc1
b2sums = 1d21307640a3844752c92e8344518daf6ad62d846689bebe6eed4dcadbf7b2e742862f5c17c0faee7256662cb75e62e124d59e5a5617e87324e1f0658f2b748d
- b2sums = dd3ce18cddc1984deffb94c999b4f086da2fac784fc09dc092efd2fb78b65200fadf90d13f6352226ebbf4aa99b4258aae722403b90ddd64eee1c942ef94114b
+ b2sums = ce2cfd5a0eab3b01415beb5ebab40baec9b713c52e8a51fab8b144996a581b3e7532fc5ff831f43fcb4167f479cb006924790a4b634190a17fac5a93f83e7953
b2sums = 7358919041a3d5df1cac50f6519b282abe7a344f07b59d52ab95a022ce0af12e743a1c194fd838b5733f3900e68c7f0756a528ac32255775f2ba830a2f052dec
b2sums = 8d7c054ff08ed6212aa81f3db6597c1e016609bdddfb19161274e5e75b0ae8b5c5501740ca6a75367d152b16f8350319bd2577561aa796cfe49840c4bd2c2d5d
- b2sums = 435120fd87d901f36f4b3ed4d7d2a6a745185feb66506981b231071aec255b2bc91c536685e5c943ce17f07f4c52ca8086c08fa5c6367dddebc4b3c2493c32ae
+ b2sums = 4f61721cb9f3f76953c552ac8c131a592418078be63f98d43bd7248422cb2d88cfc1341f64b9124c7f102bf17635bb2734bd597b7b1011cc24223a5cc5fbf233
pkgname = nvidia-390xx
pkgdesc = NVIDIA drivers for linux, 390xx legacy branch
diff --git a/PKGBUILD b/PKGBUILD
index 632b2a16fc17..be5f21967da9 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -9,7 +9,7 @@
pkgbase=nvidia-390xx
pkgname=(nvidia-390xx nvidia-390xx-dkms)
pkgver=390.138
-pkgrel=9
+pkgrel=10
pkgdesc="NVIDIA drivers for linux, 390xx legacy branch"
arch=('x86_64')
url="https://www.nvidia.com/"
@@ -23,23 +23,27 @@ source=("https://us.download.nvidia.com/XFree86/Linux-x86_64/${pkgver}/${_pkg}.r
'kernel-5.8.patch'
'kernel-5.9.patch'
'https://gitlab.com/herecura/packages/nvidia-390xx-dkms/-/raw/db83ed8ac2e0e0097d535a82e2fd4ee0f31873e3/kernel-5.10.patch'
- 'https://salsa.debian.org/nvidia-team/nvidia-graphics-drivers/-/raw/4ba8f0d0b7fc3e7c2cce19c23930b279c15280e5/debian/module/debian/patches/disable-module-nvidia-uvm.patch')
+ 'uvm_common-415.18.c')
b2sums=('bf56cef38d76accdf547b96cd444b0bd4288f604d44a1d274f3246c13310d6a59050b36f384280edb83938db81fa0544f7a2dc84ff6f440ff90b76ee62749fc1'
'1d21307640a3844752c92e8344518daf6ad62d846689bebe6eed4dcadbf7b2e742862f5c17c0faee7256662cb75e62e124d59e5a5617e87324e1f0658f2b748d'
- 'dd3ce18cddc1984deffb94c999b4f086da2fac784fc09dc092efd2fb78b65200fadf90d13f6352226ebbf4aa99b4258aae722403b90ddd64eee1c942ef94114b'
+ 'c68dc5d3bb659c4e9fc595d6e143677e58a4d564f171490a1cae85b58fb5860240d8dcafb8f314736bb5856a040d7439ce9595cefe61e3dbb7a9166ec7e76ad7'
'7358919041a3d5df1cac50f6519b282abe7a344f07b59d52ab95a022ce0af12e743a1c194fd838b5733f3900e68c7f0756a528ac32255775f2ba830a2f052dec'
'8d7c054ff08ed6212aa81f3db6597c1e016609bdddfb19161274e5e75b0ae8b5c5501740ca6a75367d152b16f8350319bd2577561aa796cfe49840c4bd2c2d5d'
- '435120fd87d901f36f4b3ed4d7d2a6a745185feb66506981b231071aec255b2bc91c536685e5c943ce17f07f4c52ca8086c08fa5c6367dddebc4b3c2493c32ae')
+ '4f61721cb9f3f76953c552ac8c131a592418078be63f98d43bd7248422cb2d88cfc1341f64b9124c7f102bf17635bb2734bd597b7b1011cc24223a5cc5fbf233')
prepare() {
sh "${_pkg}.run" --extract-only
cd "${_pkg}"
+ # Use GPL2/MIT code directly from 415.18 release
+ # https://www.nvidia.com/Download/driverResults.aspx/140282/en-us
+ install -m644 ../uvm_common-415.18.c kernel/nvidia-uvm/uvm_common.c
+
# Restore phys_to_dma support (still needed for 396.18) (and still needed for 390.138)
# https://bugs.archlinux.org/task/58074
patch -Np1 -i ../kernel-4.16.patch
- # 5.8 Patch
+ # 5.8 Patch, Alberto Milone
patch -Np1 -i ../kernel-5.8.patch
# 5.9 Patch, from loqs
@@ -49,14 +53,6 @@ prepare() {
patch -Np1 -i ../kernel-5.10.patch
cp -a kernel kernel-dkms
-
- # 5.10 disable UVM, from Debian
- pushd kernel
- patch -Np1 -i ../../disable-module-nvidia-uvm.patch
- popd
-
- cp -a kernel kernel-dkms-noUVM
-
cd kernel-dkms
sed -i "s/__VERSION_STRING/${pkgver}/" dkms.conf
sed -i 's/__JOBS/`nproc`/' dkms.conf
@@ -68,23 +64,7 @@ DEST_MODULE_LOCATION[1]="/kernel/drivers/video"\
BUILT_MODULE_NAME[2]="nvidia-modeset"\
DEST_MODULE_LOCATION[2]="/kernel/drivers/video"\
BUILT_MODULE_NAME[3]="nvidia-drm"\
-DEST_MODULE_LOCATION[3]="/kernel/drivers/video"\
-BUILD_EXCLUSIVE_KERNEL="^(4\\..*|5\\.[0-9]\\.."' dkms.conf
-
- # Gift for linux-rt guys
- sed -i 's/NV_EXCLUDE_BUILD_MODULES/IGNORE_PREEMPT_RT_PRESENCE=1 NV_EXCLUDE_BUILD_MODULES/' dkms.conf
-
- cd ../kernel-dkms-noUVM
- sed -i "s/__VERSION_STRING/${pkgver}/" dkms.conf
- sed -i 's/__JOBS/`nproc`/' dkms.conf
- sed -i 's/__DKMS_MODULES//' dkms.conf
- sed -i '$iBUILT_MODULE_NAME[0]="nvidia"\
-DEST_MODULE_LOCATION[0]="/kernel/drivers/video"\
-BUILT_MODULE_NAME[1]="nvidia-modeset"\
-DEST_MODULE_LOCATION[1]="/kernel/drivers/video"\
-BUILT_MODULE_NAME[2]="nvidia-drm"\
-DEST_MODULE_LOCATION[2]="/kernel/drivers/video"\
-BUILD_EXCLUSIVE_KERNEL="^5\\.10\\..*"' dkms.conf
+DEST_MODULE_LOCATION[3]="/kernel/drivers/video"' dkms.conf
# Gift for linux-rt guys
sed -i 's/NV_EXCLUDE_BUILD_MODULES/IGNORE_PREEMPT_RT_PRESENCE=1 NV_EXCLUDE_BUILD_MODULES/' dkms.conf
@@ -101,7 +81,7 @@ package_nvidia-390xx() {
_extradir="/usr/lib/modules/$(</usr/src/linux/version)/extramodules"
install -Dt "${pkgdir}${_extradir}" -m644 \
- "${srcdir}/${_pkg}/kernel"/nvidia{,-modeset,-drm}.ko
+ "${srcdir}/${_pkg}/kernel"/nvidia{,-modeset,-drm,-uvm}.ko
find "${pkgdir}" -name '*.ko' -exec gzip -n {} +
@@ -121,7 +101,6 @@ package_nvidia-390xx-dkms() {
install -dm 755 "${pkgdir}"/usr/src
cp -dr --no-preserve='ownership' kernel-dkms "${pkgdir}/usr/src/nvidia-${pkgver}"
- cp -dr --no-preserve='ownership' kernel-dkms-noUVM "${pkgdir}/usr/src/nvidia-noUVM-${pkgver}"
echo "blacklist nouveau" |
install -Dm644 /dev/stdin "${pkgdir}/usr/lib/modprobe.d/${pkgname}.conf"
diff --git a/kernel-5.8.patch b/kernel-5.8.patch
index 31bc0e685baf..b936e274219d 100644
--- a/kernel-5.8.patch
+++ b/kernel-5.8.patch
@@ -2,6 +2,13 @@ From: Alberto Milone <alberto.milone@canonical.com>
Date: Mon, 13 Jul 2020 14:04:41 +0200
Subject: [PATCH 1/1] Add support for Linux 5.8
+---
+ kernel/common/inc/nv-linux.h | 4 ++++
+ kernel/common/inc/nv-mm.h | 4 ++++
+ kernel/conftest.sh | 38 ++++++++++++++++++++++++++++++++++++++
+ kernel/nvidia/nvidia.Kbuild | 2 ++
+ 4 files changed, 48 insertions(+)
+
diff --git a/kernel/common/inc/nv-linux.h b/kernel/common/inc/nv-linux.h
index ac5bb95..3d2f0b7 100644
--- a/kernel/common/inc/nv-linux.h
@@ -98,3 +105,4 @@ index 63e369f..656db7b 100644
NV_CONFTEST_TYPE_COMPILE_TESTS += pci_dev_has_skip_bus_pm
--
2.25.1
+
diff --git a/uvm_common-415.18.c b/uvm_common-415.18.c
new file mode 100644
index 000000000000..fe227514ca89
--- /dev/null
+++ b/uvm_common-415.18.c
@@ -0,0 +1,378 @@
+/*******************************************************************************
+ Copyright (c) 2013-2018 NVIDIA Corporation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*******************************************************************************/
+
+/*
+ * This file contains code that is common to all variants of the (Linux) UVM
+ * kernel module.
+ */
+
+#include "uvm_common.h"
+#include "uvm_linux.h"
+
+// TODO: Bug 1766109: Remove this when the GPU event stubs are no longer needed
+#include "nv_uvm_interface.h"
+
+#include "uvm8_init.h"
+#include "uvm8_forward_decl.h"
+
+// TODO: Bug 1710855: Tweak this number through benchmarks
+#define UVM_SPIN_LOOP_SCHEDULE_TIMEOUT_NS (10*1000ULL)
+#define UVM_SPIN_LOOP_PRINT_TIMEOUT_SEC 30ULL
+
+static dev_t g_uvmBaseDev;
+struct UvmOpsUvmEvents g_exportedUvmOps;
+
+static char* uvm_driver_mode = "8";
+
+// There used to be other choices, but now there is only one driver mode: 8.
+// If no more choices show up soon, we may just delete this module parameter
+// entirely.
+module_param(uvm_driver_mode, charp, S_IRUGO);
+MODULE_PARM_DESC(uvm_driver_mode,
+ "Set the uvm kernel driver mode. Choices include: 8");
+
+// Default to debug prints being enabled for debug and develop builds and
+// disabled for release builds.
+static int uvm_debug_prints = UVM_IS_DEBUG() || UVM_IS_DEVELOP();
+
+// Make the module param writable so that prints can be enabled or disabled at
+// any time by modifying the module parameter.
+module_param(uvm_debug_prints, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(uvm_debug_prints, "Enable uvm debug prints.");
+
+bool uvm_debug_prints_enabled()
+{
+ return uvm_debug_prints != 0;
+}
+
+typedef enum
+{
+ UVM_DRIVER_MODE_8,
+} UvmDriverMode;
+
+static const char * uvm_driver_mode_to_string(UvmDriverMode uvmDriverMode)
+{
+ switch (uvmDriverMode)
+ {
+ case UVM_DRIVER_MODE_8:
+ return "8";
+ }
+ return "invalid";
+}
+
+static UvmDriverMode uvm_get_mode(void)
+{
+ static NvBool bUvmDriverModeChecked = NV_FALSE;
+ static UvmDriverMode uvmDriverMode;
+
+ if (!bUvmDriverModeChecked)
+ {
+ uvmDriverMode = UVM_DRIVER_MODE_8;
+
+ bUvmDriverModeChecked = NV_TRUE;
+ }
+
+ return uvmDriverMode;
+}
+
+NV_STATUS uvm_api_initialize(UVM_INITIALIZE_PARAMS *params, struct file *filp)
+{
+ params->rmStatus = uvm8_initialize(params, filp);
+ return params->rmStatus;
+}
+
+// This function serves to 'stub' out functionality by being a No-Op and
+// returning NV_OK early.
+NV_STATUS uvm_api_stub(void *pParams, struct file *filp)
+{
+ return NV_OK;
+}
+
+// This function serves to identify functionality that isn't supported
+// in this UVM driver, by returning NV_ERR_NOT_SUPPORTED early.
+NV_STATUS uvm_api_unsupported(void *pParams, struct file *filp)
+{
+ return NV_ERR_NOT_SUPPORTED;
+}
+
+//
+// TODO: Bug 1766109: uvm8: delete UVM-Lite files and remove -lite mode
+// ...just remove -lite mode, instead of the original to-do: which was:
+//
+static int __init uvm_init(void)
+{
+ NvBool allocated_dev = NV_FALSE;
+
+ // The various helper init routines will create their own minor devices, so
+ // we only need to create space for them here.
+ int ret = alloc_chrdev_region(&g_uvmBaseDev,
+ 0,
+ NVIDIA_UVM_NUM_MINOR_DEVICES,
+ NVIDIA_UVM_DEVICE_NAME);
+ if (ret != 0) {
+ UVM_ERR_PRINT("alloc_chrdev_region failed: %d\n", ret);
+ goto error;
+ }
+ allocated_dev = NV_TRUE;
+
+ ret = uvm8_init(g_uvmBaseDev);
+
+ if (ret != 0) {
+ UVM_ERR_PRINT("uvm init failed: %d\n", ret);
+ goto error;
+ }
+
+ pr_info("Loaded the UVM driver in %s mode, major device number %d\n",
+ uvm_driver_mode_to_string(uvm_get_mode()), MAJOR(g_uvmBaseDev));
+
+ if (uvm_enable_builtin_tests)
+ pr_info("Built-in UVM tests are enabled. This is a security risk.\n");
+
+ return 0;
+
+error:
+ if (allocated_dev)
+ unregister_chrdev_region(g_uvmBaseDev, NVIDIA_UVM_NUM_MINOR_DEVICES);
+
+ return ret;
+}
+
+static void __exit uvm_exit(void)
+{
+ uvm8_exit();
+
+ unregister_chrdev_region(g_uvmBaseDev, NVIDIA_UVM_NUM_MINOR_DEVICES);
+
+ pr_info("Unloaded the UVM driver in %s mode\n", uvm_driver_mode_to_string(uvm_get_mode()));
+}
+
+//
+// Convert kernel errno codes to corresponding NV_STATUS
+//
+NV_STATUS errno_to_nv_status(int errnoCode)
+{
+ if (errnoCode < 0)
+ errnoCode = -errnoCode;
+
+ switch (errnoCode)
+ {
+ case 0:
+ return NV_OK;
+
+ case E2BIG:
+ case EINVAL:
+ return NV_ERR_INVALID_ARGUMENT;
+
+ case EACCES:
+ return NV_ERR_INVALID_ACCESS_TYPE;
+
+ case EADDRINUSE:
+ case EADDRNOTAVAIL:
+ return NV_ERR_UVM_ADDRESS_IN_USE;
+
+ case EFAULT:
+ return NV_ERR_INVALID_ADDRESS;
+
+ case EINTR:
+ case EBUSY:
+ return NV_ERR_BUSY_RETRY;
+
+ case ENXIO:
+ case ENODEV:
+ return NV_ERR_MODULE_LOAD_FAILED;
+
+ case ENOMEM:
+ return NV_ERR_NO_MEMORY;
+
+ case EPERM:
+ return NV_ERR_INSUFFICIENT_PERMISSIONS;
+
+ case ESRCH:
+ return NV_ERR_PID_NOT_FOUND;
+
+ case ETIMEDOUT:
+ return NV_ERR_TIMEOUT;
+
+ case EEXIST:
+ return NV_ERR_IN_USE;
+
+ case ENOSYS:
+ return NV_ERR_NOT_SUPPORTED;
+
+ case ENOENT:
+ return NV_ERR_NO_VALID_PATH;
+
+ case EIO:
+ return NV_ERR_RC_ERROR;
+
+ default:
+ return NV_ERR_GENERIC;
+ };
+}
+
+// Returns POSITIVE errno
+int nv_status_to_errno(NV_STATUS status)
+{
+ switch (status) {
+ case NV_OK:
+ return 0;
+
+ case NV_ERR_BUSY_RETRY:
+ return EBUSY;
+
+ case NV_ERR_INSUFFICIENT_PERMISSIONS:
+ return EPERM;
+
+ case NV_ERR_GPU_UUID_NOT_FOUND:
+ return ENODEV;
+
+ case NV_ERR_INSUFFICIENT_RESOURCES:
+ case NV_ERR_NO_MEMORY:
+ return ENOMEM;
+
+ case NV_ERR_INVALID_ACCESS_TYPE:
+ return EACCES;
+
+ case NV_ERR_INVALID_ADDRESS:
+ return EFAULT;
+
+ case NV_ERR_INVALID_ARGUMENT:
+ case NV_ERR_INVALID_DEVICE:
+ case NV_ERR_INVALID_PARAMETER:
+ case NV_ERR_INVALID_REQUEST:
+ case NV_ERR_INVALID_STATE:
+ return EINVAL;
+
+ case NV_ERR_NOT_SUPPORTED:
+ return ENOSYS;
+
+ case NV_ERR_MODULE_LOAD_FAILED:
+ return ENXIO;
+
+ case NV_ERR_OVERLAPPING_UVM_COMMIT:
+ case NV_ERR_UVM_ADDRESS_IN_USE:
+ return EADDRINUSE;
+
+ case NV_ERR_PID_NOT_FOUND:
+ return ESRCH;
+
+ case NV_ERR_TIMEOUT:
+ case NV_ERR_TIMEOUT_RETRY:
+ return ETIMEDOUT;
+
+ case NV_ERR_IN_USE:
+ return EEXIST;
+
+ case NV_ERR_NO_VALID_PATH:
+ return ENOENT;
+
+ case NV_ERR_RC_ERROR:
+ case NV_ERR_ECC_ERROR:
+ return EIO;
+
+ default:
+ UVM_ASSERT_MSG(0, "No errno conversion set up for NV_STATUS %s\n", nvstatusToString(status));
+ return EINVAL;
+ }
+}
+
+//
+// This routine retrieves the process ID of current, but makes no attempt to
+// refcount or lock the pid in place, because that capability is only available
+// to GPL-licenses device drivers.
+//
+// TODO: Bug 1483843: Use the GPL-protected routines if and when we are able to
+// change over to a dual MIT/GPL license.
+//
+unsigned uvm_get_stale_process_id(void)
+{
+ return (unsigned) current->tgid;
+}
+
+unsigned uvm_get_stale_thread_id(void)
+{
+ return (unsigned) current->pid;
+}
+
+//
+// A simple security rule for allowing access to UVM user space memory: if you
+// are the same user as the owner of the memory, or if you are root, then you
+// are granted access. The idea is to allow debuggers and profilers to work, but
+// without opening up any security holes.
+//
+NvBool uvm_user_id_security_check(uid_t euidTarget)
+{
+ return (NV_CURRENT_EUID() == euidTarget) ||
+ (UVM_ROOT_UID == euidTarget);
+}
+
+void on_uvm_assert(void)
+{
+ (void)NULL;
+}
+
+NV_STATUS uvm_spin_loop(uvm_spin_loop_t *spin)
+{
+ NvU64 curr = NV_GETTIME();
+
+ // This schedule() is required for functionality, not just system
+ // performance. It allows RM to run and unblock the UVM driver:
+ //
+ // - UVM must service faults in order for RM to idle/preempt a context
+ // - RM must service interrupts which stall UVM (SW methods, stalling CE
+ // interrupts, etc) in order for UVM to service faults
+ //
+ // Even though UVM's bottom half is preemptable, we have encountered cases
+ // in which a user thread running in RM won't preempt the UVM driver's
+ // thread unless the UVM driver thread gives up its timeslice. This is also
+ // theoretically possible if the RM thread has a low nice priority.
+ //
+ // TODO: Bug 1710855: Look into proper prioritization of these threads as a longer-term
+ // solution.
+ if (curr - spin->start_time_ns >= UVM_SPIN_LOOP_SCHEDULE_TIMEOUT_NS && NV_MAY_SLEEP()) {
+ schedule();
+ curr = NV_GETTIME();
+ }
+
+ cpu_relax();
+
+ // TODO: Bug 1710855: Also check fatal_signal_pending() here if the caller can handle it.
+
+ if (curr - spin->print_time_ns >= 1000*1000*1000*UVM_SPIN_LOOP_PRINT_TIMEOUT_SEC) {
+ spin->print_time_ns = curr;
+ return NV_ERR_TIMEOUT_RETRY;
+ }
+
+ return NV_OK;
+}
+
+module_init(uvm_init);
+module_exit(uvm_exit);
+
+// This parameter allows a program in user mode to call the kernel tests
+// defined in this module. This parameter should only be used for testing and
+// must not be set to true otherwise since it breaks security when it is
+// enabled. By default and for safety reasons this parameter is set to false.
+int uvm_enable_builtin_tests = 0;
+module_param(uvm_enable_builtin_tests, int, S_IRUGO);
+MODULE_PARM_DESC(uvm_enable_builtin_tests,
+ "Enable the UVM built-in tests. (This is a security risk)");
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_INFO(supported, "external");