diff --git a/ABI_VERSION b/ABI_VERSION
new file mode 100644
index 0000000000..9a7c1e503f
--- /dev/null
+++ b/ABI_VERSION
@@ -0,0 +1 @@
+20.0
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 8d0b9e0cd0..6ef2c5721c 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -14,3 +14,6 @@ if python3.found()
 else
 	map_to_def_cmd = ['meson', 'runpython', files('map_to_def.py')]
 endif
+
+# stable ABI always starts with "DPDK_"
+is_experimental_cmd = [find_program('grep', 'findstr'), '^DPDK_']
diff --git a/config/meson.build b/config/meson.build
index 2b1cb92e7e..3ffb73ab9c 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -18,6 +18,11 @@ endforeach
 # depending on the configuration options
 pver = meson.project_version().split('.')
 major_version = '@0@.@1@'.format(pver.get(0), pver.get(1))
+abi_version = run_command(find_program('cat', 'more'),
+	files('../ABI_VERSION')).stdout().strip()
+# experimental libraries are versioned as 0.majorminor versions, e.g. 0.201
+ever = abi_version.split('.')
+experimental_abi_version = '0.@0@@1@'.format(ever.get(0), ever.get(1))
 
 # extract all version information into the build configuration
 dpdk_conf.set('RTE_VER_YEAR', pver.get(0).to_int())
@@ -37,7 +42,7 @@ endif
 
 pmd_subdir_opt = get_option('drivers_install_subdir')
 if pmd_subdir_opt.contains('<VERSION>')
-	pmd_subdir_opt = major_version.join(pmd_subdir_opt.split('<VERSION>'))
+	pmd_subdir_opt = abi_version.join(pmd_subdir_opt.split('<VERSION>'))
 endif
 driver_install_path = join_paths(get_option('libdir'), pmd_subdir_opt)
 eal_pmd_path = join_paths(get_option('prefix'), driver_install_path)
diff --git a/doc/guides/contributing/abi_versioning.rst b/doc/guides/contributing/abi_versioning.rst
index 050c971dd8..a21f4e7a41 100644
--- a/doc/guides/contributing/abi_versioning.rst
+++ b/doc/guides/contributing/abi_versioning.rst
@@ -111,16 +111,17 @@ how this may be done.
  ...
 
 At the same time, the major ABI version is changed atomically across all
-libraries by incrementing the major version in individual library's soname, e.g.
-``libacl.so.21``. This is done by bumping the LIBABIVER number in the libraries
-Makefile to indicate to dynamic linking applications that this is a later, and
-possibly incompatible library version:
+libraries by incrementing the major version in the ABI_VERSION file. This is
+done globally for all libraries that declare a stable ABI. For libraries marked
+as EXPERIMENTAL, their major ABI version is always set to 0.
 
-.. code-block:: c
-
-   -LIBABIVER := 20
-   +LIBABIVER := 21
+Minor ABI versions
+~~~~~~~~~~~~~~~~~~
 
+Each non-LTS release will also increment minor ABI version, to permit multiple
+DPDK versions being installed alongside each other. Both stable and
+experimental ABI's are versioned using the global version file that is updated
+at the start of each release cycle, and are managed at the project level.
 
 Versioning Macros
 -----------------
diff --git a/doc/guides/contributing/coding_style.rst b/doc/guides/contributing/coding_style.rst
index a6843de5ad..841ef6d5c8 100644
--- a/doc/guides/contributing/coding_style.rst
+++ b/doc/guides/contributing/coding_style.rst
@@ -803,9 +803,8 @@ lpm, etc. For drivers, the same format of Makefile is used.
 	CFLAGS += -O3
 	CFLAGS += $(WERROR_FLAGS)
 
-	# the symbol version information for the library, and .so version
+	# the symbol version information for the library
 	EXPORT_MAP := rte_<name>_version.map
-	LIBABIVER := 1
 
 	# all source filenames are stored in SRCS-y
 	SRCS-$(CONFIG_RTE_LIBRTE_<NAME>) += rte_<name>.c
@@ -955,11 +954,6 @@ use_function_versioning
 	twice with suitable parameters for each of shared or static library
 	builds.
 
-version
-	**Default Value = 1**.
-	Specifies the ABI version of the library, and is used as the major
-	version number of the resulting ``.so`` library.
-
 Meson Build File Contents - Drivers
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/meson.build b/drivers/meson.build
index 823e3ef839..80cc7001fe 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -41,7 +41,6 @@ foreach class:dpdk_driver_classes
 		build = true # set to false to disable, e.g. missing deps
 		reason = '<unknown reason>' # set if build == false to explain
 		name = drv
-		version = 1
 		allow_experimental_apis = false
 		sources = []
 		objs = []
@@ -124,12 +123,19 @@ foreach class:dpdk_driver_classes
 					output: out_filename,
 					depends: [pmdinfogen, tmp_lib])
 
-			if get_option('per_library_versions')
-				lib_version = '@0@.1'.format(version)
-				so_version = '@0@'.format(version)
+			version_map = '@0@/@1@/@2@_version.map'.format(
+					meson.current_source_dir(),
+					drv_path, lib_name)
+
+			is_experimental = run_command(is_experimental_cmd,
+				files(version_map)).returncode()
+
+			if is_experimental != 0
+				lib_version = experimental_abi_version
+				so_version = experimental_abi_version
 			else
-				lib_version = major_version
-				so_version = major_version
+				lib_version = abi_version
+				so_version = abi_version
 			endif
 
 			# now build the static driver
diff --git a/lib/meson.build b/lib/meson.build
index bc8eb1d218..6ceb5e756e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -47,7 +47,6 @@ foreach l:libraries
 	build = true
 	reason = '<unknown reason>' # set if build == false to explain why
 	name = l
-	version = 1
 	allow_experimental_apis = false
 	use_function_versioning = false
 	sources = []
@@ -106,12 +105,18 @@ foreach l:libraries
 				cflags += '-DRTE_USE_FUNCTION_VERSIONING'
 			endif
 
-			if get_option('per_library_versions')
-				lib_version = '@0@.1'.format(version)
-				so_version = '@0@'.format(version)
+			version_map = '@0@/@1@/rte_@2@_version.map'.format(
+					meson.current_source_dir(), dir_name, name)
+
+			is_experimental = run_command(is_experimental_cmd,
+					files(version_map)).returncode()
+
+			if is_experimental != 0
+				lib_version = experimental_abi_version
+				so_version = experimental_abi_version
 			else
-				lib_version = major_version
-				so_version = major_version
+				lib_version = abi_version
+				so_version = abi_version
 			endif
 
 			# first build static lib
diff --git a/meson_options.txt b/meson_options.txt
index e6fcb884be..bc369d06c9 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -28,8 +28,6 @@ option('max_lcores', type: 'integer', value: 128,
 	description: 'maximum number of cores/threads supported by EAL')
 option('max_numa_nodes', type: 'integer', value: 4,
 	description: 'maximum number of NUMA nodes supported by EAL')
-option('per_library_versions', type: 'boolean', value: true,
-	description: 'true: each lib gets its own version number, false: DPDK version used for each lib')
 option('tests', type: 'boolean', value: true,
 	description: 'build unit tests')
 option('use_hpet', type: 'boolean', value: false,
diff --git a/mk/rte.lib.mk b/mk/rte.lib.mk
index 4df8849a08..3b318a5306 100644
--- a/mk/rte.lib.mk
+++ b/mk/rte.lib.mk
@@ -11,20 +11,16 @@ EXTLIB_BUILD ?= n
 # VPATH contains at least SRCDIR
 VPATH += $(SRCDIR)
 
-ifneq ($(CONFIG_RTE_MAJOR_ABI),)
-ifneq ($(LIBABIVER),)
-LIBABIVER := $(CONFIG_RTE_MAJOR_ABI)
-endif
+ifneq ($(shell grep -s "^DPDK_" $(SRCDIR)/$(EXPORT_MAP)),)
+LIBABIVER := $(shell cat $(RTE_SRCDIR)/ABI_VERSION)
+else
+# EXPERIMENTAL ABI is versioned as 0.major+minor, e.g. 0.201 for 20.1 ABI
+LIBABIVER := 0.$(shell cat $(RTE_SRCDIR)/ABI_VERSION | td -d '.')
 endif
 
 ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
 LIB := $(patsubst %.a,%.so.$(LIBABIVER),$(LIB))
 ifeq ($(EXTLIB_BUILD),n)
-ifeq ($(CONFIG_RTE_MAJOR_ABI),)
-ifeq ($(CONFIG_RTE_NEXT_ABI),y)
-LIB := $(LIB).1
-endif
-endif
 CPU_LDFLAGS += --version-script=$(SRCDIR)/$(EXPORT_MAP)
 endif
 endif
