bugfix: Fix errors related to removal of kernel modules 51/11351/3
authorMartin Klozik <martinx.klozik@intel.com>
Tue, 15 Mar 2016 15:30:42 +0000 (15:30 +0000)
committerMaryam Tahhan <maryam.tahhan@intel.com>
Mon, 21 Mar 2016 15:30:24 +0000 (15:30 +0000)
Internal module manager is responsible for removal of all vsperf
specific kernel modules. Order of modules has been changed
to respect possible dependencies among modules. Manager has been
improved to detect modules automatically loaded by modprobe
to solve dependecies. These modules are automatically removed.
Code of module manager has been refactored.

Change-Id: I4484f3bea7d9900db688f96edd5c3c6d4a304742
JIRA: VSPERF-259
Signed-off-by: Martin Klozik <martinx.klozik@intel.com>
Reviewed-by: Maryam Tahhan <maryam.tahhan@intel.com>
Reviewed-by: Al Morton <acmorton@att.com>
Reviewed-by: Christian Trautman <ctrautma@redhat.com>
Reviewed-by: Brian Castelli <brian.castelli@spirent.com>
src/dpdk/dpdk.py
tools/module_manager.py

index 127ecaf..f8cbbd8 100644 (file)
@@ -111,13 +111,14 @@ def insert_vhost_modules():
     mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
                                    'lib',
                                    'librte_vhost')
-    _DPDK_MODULE_MANAGER.insert_module_group('VHOST_MODULE', mod_path_prefix)
+    _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('VHOST_MODULE'), mod_path_prefix)
 
 
 def remove_vhost_modules():
     """Removes all VHOST related kernel modules
     """
-    _DPDK_MODULE_MANAGER.remove_module_group(settings.getValue('VHOST_MODULE'))
+    # all modules are removed automatically by _remove_modules() method
+    pass
 
 
 #
index 565bac5..2eb4c63 100644 (file)
@@ -20,7 +20,6 @@ import logging
 
 from tools import tasks
 
-_LOGGER = logging.getLogger(__name__)
 class ModuleManager(object):
     """Simple module manager which acts as system wrapper for Kernel Modules.
     """
@@ -32,119 +31,115 @@ class ModuleManager(object):
         """
         self._modules = []
 
-    def insert_modules(self, modules):
-        """Method inserts list of modules. In case that module name ends
-        with .ko suffix then insmod will be used for its insertion. Otherwise
-        modprobe will be called.
+    def insert_module(self, module):
+        """Method inserts given module.
+
+        In case that module name ends with .ko suffix then insmod will
+        be used for its insertion. Otherwise modprobe will be called.
 
-        :returns: None
+        :param module: a name of kernel module
         """
+        module_base_name = os.path.basename(os.path.splitext(module)[0])
 
-        for module in modules:
-            if self.is_module_inserted(module):
-                continue
+        if self.is_module_inserted(module):
+            self._logger.info('Module already loaded \'%s\'.', module_base_name)
+            # add it to internal list, so we can try to remove it at the end
+            self._modules.append(module)
+            return
+
+        try:
+            if module.endswith('.ko'):
+                tasks.run_task(['sudo', 'insmod', module], self._logger,
+                               'Insmod module \'%s\'...' % module_base_name, True)
+            else:
+                tasks.run_task(['sudo', 'modprobe', module], self._logger,
+                               'Modprobe module \'%s\'...' % module_base_name, True)
+            self._modules.append(module)
+        except subprocess.CalledProcessError:
+            # in case of error, show full module name
+            self._logger.error('Unable to insert module \'%s\'.', module)
+            raise  # fail catastrophically
 
-            try:
-                if module.endswith('.ko'):
-                    tasks.run_task(['sudo', 'insmod', module], self._logger,
-                                   'Insmod module \'%s\'...' % module, True)
-                else:
-                    tasks.run_task(['sudo', 'modprobe', module], self._logger,
-                                   'Modprobe module \'%s\'...' % module, True)
-                _LOGGER.info("Inserted Module %s", module)
-                self._modules.append(module)
-            except subprocess.CalledProcessError:
-                self._logger.error('Unable to insert module \'%s\'.', module)
-                raise  # fail catastrophically
+    def insert_modules(self, modules):
+        """Method inserts list of modules.
 
-    def insert_module_group(self, module_group, group_path_prefix):
+        :param modules: a list of modules to be inserted
+        """
+        for module in modules:
+            self.insert_module(module)
+
+    def insert_module_group(self, module_group, path_prefix):
         """Ensure all modules in a group are inserted into the system.
 
         :param module_group: A name of configuration item containing a list
-        of module names
+            of module names
+        :param path_prefix: A name of directory which contains given
+            group of modules
+        """
+        for (path_suffix, module) in module_group:
+            self.insert_module(os.path.join(path_prefix, path_suffix, '%s.ko' % module))
+
+    def remove_module(self, module):
+        """Removes a single module.
+
+        :param module: a name of kernel module
         """
-        for module in module_group:
-            # first check if module is loaded
-            if self.is_module_inserted(module[1]):
-                continue
+        if self.is_module_inserted(module):
+            # get module base name, i.e strip path and .ko suffix if possible
+            module_base_name = os.path.basename(os.path.splitext(module)[0])
 
             try:
-                mod_path = os.path.join(group_path_prefix, module[0],
-                                        '%s.ko' % module[1])
-                tasks.run_task(['sudo', 'insmod', mod_path], _LOGGER,
-                               'Inserting module \'%s\'...' % module[1], True)
-                self._modules.append(module)
+                self._logger.info('Removing module \'%s\'...', module_base_name)
+                subprocess.check_call('sudo rmmod {}'.format(module_base_name),
+                                      shell=True, stderr=subprocess.DEVNULL)
+                # in case that module was loaded automatically by modprobe
+                # to solve dependecies, then it is not in internal list of modules
+                if module in self._modules:
+                    self._modules.remove(module)
             except subprocess.CalledProcessError:
-                _LOGGER.error('Unable to insert module \'%s\'.', module[1])
-                raise  # fail catastrophically
+                # in case of error, show full module name...
+                self._logger.info('Unable to remove module \'%s\'.', module)
+                # ...and list of dependend modules, if there are any
+                module_details = self.get_module_details(module_base_name)
+                if module_details:
+                    mod_dep = module_details.split(' ')[3].rstrip(',')
+                    if mod_dep[0] != '-':
+                        self._logger.debug('Module \'%s\' is used by module(s) \'%s\'.',
+                                           module_base_name, mod_dep)
 
     def remove_modules(self):
         """Removes all modules that have been previously inserted.
         """
-        for module in self._modules:
-            # first check if module is loaded
-            if not self.is_module_inserted(module):
-                continue
+        # remove modules in reverse order to respect their dependencies
+        for module in reversed(self._modules):
+            self.remove_module(module)
 
-            try:
-                # rmmod supports both simple module name and full module path
-                # with .ko suffix
-                tasks.run_task(['sudo', 'rmmod', module], self._logger,
-                               'Removing module \'%s\'...' % module, True)
-                self._modules.remove(module)
-            except subprocess.CalledProcessError:
-                self._logger.error('Unable to remove module \'%s\'.', module)
-                continue
-    @staticmethod
-    def is_module_inserted(module):
+    def is_module_inserted(self, module):
         """Check if a module is inserted on system.
+
+        :param module: a name of kernel module
         """
-        if module.endswith('.ko'):
-        # get module base name, i.e strip path and .ko suffix if possible
-            module_base_name = os.path.basename(os.path.splitext(module)[0])
-        else:
-            module_base_name = module
+        module_base_name = os.path.basename(os.path.splitext(module)[0])
+
+        return self.get_module_details(module_base_name) != None
+
+    @staticmethod
+    def get_module_details(module):
+        """Return details about given module
 
+        :param module: a name of kernel module
+        :returns: In case that module is loaded in OS, then corresponding
+            line from /proc/modules will be returned. Otherwise it returns None.
+        """
         # get list of modules from kernel
         with open('/proc/modules') as mod_file:
             loaded_mods = mod_file.readlines()
 
-        # first check if module is loaded
+        # check if module is loaded
         for line in loaded_mods:
-            if line.startswith(module_base_name):
-                return True
-        return False
+            # underscores '_' and dashes '-' in module names are interchangeable, so we
+            # have to normalize module names before comparision
+            if line.split(' ')[0].replace('-', '_') == module.replace('-', '_'):
+                return line
 
-    def remove_module(self, module):
-        """Removes a single module.
-        """
-        if self.is_module_inserted(module):
-            # get module base name, i.e strip path and .ko suffix if possible
-            module_base_name = os.path.basename(os.path.splitext(module)[0])
-
-            try:
-                # rmmod supports both simple module name and full module path
-                # with .ko suffix
-                tasks.run_task(['sudo', 'rmmod', module_base_name], self._logger,
-                               'Removing module \'%s\'...' % module, True)
-                self._modules.remove(module)
-            except subprocess.CalledProcessError:
-                self._logger.error('Unable to remove module \'%s\'.', module_base_name)
-
-    def remove_module_group(self, module_group):
-        """Removes all modules in the modules group.
-        """
-        for module in module_group:
-            if not self.is_module_inserted(module[1]):
-                continue
-            # get module base name, i.e strip path and .ko suffix if possible
-            module_base_name = os.path.basename(os.path.splitext(module)[0])
-
-            try:
-                # rmmod supports both simple module name and full module path
-                # with .ko suffix
-                tasks.run_task(['sudo', 'rmmod', module_base_name], self._logger,
-                               'Removing module \'%s\'...' % module, True)
-                self._modules.remove(module)
-            except subprocess.CalledProcessError:
-                self._logger.error('Unable to remove module \'%s\'.', module_base_name)
+        return None