diff --git a/lib/locking.py b/lib/locking.py
index 7c886a5da64d9f0cbfdfb52ab4b5b14cce189974..b251f30ace7e26b668b245ab46cd17a4f54ef404 100644
--- a/lib/locking.py
+++ b/lib/locking.py
@@ -372,11 +372,25 @@ class LockSet:
     # Check we don't already own locks at this level
     assert not self._is_owned(), "Cannot acquire locks in the same set twice"
 
+    if names is None:
+      # If no names are given acquire the whole set by not letting new names
+      # being added before we release, and getting the current list of names.
+      # Some of them may then be deleted later, but we'll cope with this.
+      #
+      # We'd like to acquire this lock in a shared way, as it's nice if
+      # everybody else can use the instances at the same time. If are acquiring
+      # them exclusively though they won't be able to do this anyway, though,
+      # so we'll get the list lock exclusively as well in order to be able to
+      # do add() on the set while owning it.
+      self.__lock.acquire(shared=shared)
+
     try:
       # Support passing in a single resource to acquire rather than many
       if isinstance(names, basestring):
         names = [names]
       else:
+        if names is None:
+          names = self.__names()
         names.sort()
 
       acquire_list = []
@@ -388,7 +402,12 @@ class LockSet:
           lock = self.__lockdict[lname] # raises KeyError if the lock is not there
           acquire_list.append((lname, lock))
         except (KeyError):
-          raise errors.LockError('non-existing lock in set (%s)' % lname)
+          if self.__lock._is_owned():
+            # We are acquiring all the set, it doesn't matter if this particular
+            # element is not there anymore.
+            continue
+          else:
+            raise errors.LockError('non-existing lock in set (%s)' % lname)
 
       # This will hold the locknames we effectively acquired.
       acquired = set()
@@ -411,13 +430,21 @@ class LockSet:
             raise
 
         except (errors.LockError):
-          name_fail = lname
-          for lname in self._list_owned():
-            self.__lockdict[lname].release()
-            self._del_owned(lname)
-          raise errors.LockError('non-existing lock in set (%s)' % name_fail)
+          if self.__lock._is_owned():
+            # We are acquiring all the set, it doesn't matter if this particular
+            # element is not there anymore.
+            continue
+          else:
+            name_fail = lname
+            for lname in self._list_owned():
+              self.__lockdict[lname].release()
+              self._del_owned(lname)
+            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
 
     except:
+      # If something went wrong and we had the set-lock let's release it...
+      if self.__lock._is_owned():
+        self.__lock.release()
       raise
 
     return acquired
@@ -448,6 +475,11 @@ class LockSet:
                "release() on unheld resources %s" %
                names.difference(self._list_owned()))
 
+    # First of all let's release the "all elements" lock, if set.
+    # After this 'add' can work again
+    if self.__lock._is_owned():
+      self.__lock.release()
+
     for lockname in names:
       # If we are sure the lock doesn't leave __lockdict without being
       # exclusively held we can do this...
@@ -463,13 +495,21 @@ class LockSet:
       shared: is the pre-acquisition shared?
 
     """
+
+    assert not self.__lock._is_owned(shared=1), (
+           "Cannot add new elements while sharing the set-lock")
+
     # Support passing in a single resource to add rather than many
     if isinstance(names, basestring):
       names = [names]
 
-    # Acquire the internal lock in an exclusive way, so there cannot be a
-    # conflicting add()
-    self.__lock.acquire()
+    # If we don't already own the set-level lock acquire it in an exclusive way
+    # we'll get it and note we need to release it later.
+    release_lock = False
+    if not self.__lock._is_owned():
+      release_lock = True
+      self.__lock.acquire()
+
     try:
       invalid_names = set(self.__names()).intersection(names)
       if invalid_names:
@@ -499,7 +539,9 @@ class LockSet:
         self.__lockdict[lockname] = lock
 
     finally:
-      self.__lock.release()
+      # Only release __lock if we were not holding it previously.
+      if release_lock:
+        self.__lock.release()
 
     return True
 
diff --git a/test/ganeti.locking_unittest.py b/test/ganeti.locking_unittest.py
index 74524ed0733bb2d05b4521d044a1d1e50771bfe0..996c8c2b8ee921b21027072fc7f49d50e8d2b5a1 100755
--- a/test/ganeti.locking_unittest.py
+++ b/test/ganeti.locking_unittest.py
@@ -331,6 +331,19 @@ class TestLockSet(unittest.TestCase):
     # Cannot remove 'three' as we are sharing it
     self.assertRaises(AssertionError, self.ls.remove, 'three')
 
+  def testAcquireSetLock(self):
+    # acquire the set-lock exclusively
+    self.assertEquals(self.ls.acquire(None), set(['one', 'two', 'three']))
+    # I can still add/remove elements...
+    self.assertEquals(self.ls.remove(['two', 'three']), ['two', 'three'])
+    self.assert_(self.ls.add('six'))
+    self.ls.release()
+    # share the set-lock
+    self.assertEquals(self.ls.acquire(None, shared=1), set(['one', 'six']))
+    # adding new elements is not possible
+    self.assertRaises(AssertionError, self.ls.add, 'five')
+    self.ls.release()
+
   def _doLockSet(self, set, shared):
     try:
       self.ls.acquire(set, shared=shared)
@@ -339,6 +352,14 @@ class TestLockSet(unittest.TestCase):
     except errors.LockError:
       self.done.put('ERR')
 
+  def _doAddSet(self, set):
+    try:
+      self.ls.add(set, acquired=1)
+      self.done.put('DONE')
+      self.ls.release()
+    except errors.LockError:
+      self.done.put('ERR')
+
   def _doRemoveSet(self, set):
     self.done.put(self.ls.remove(set))
 
@@ -412,6 +433,42 @@ class TestLockSet(unittest.TestCase):
     self.assertEqual(self.done.get(True, 1), ['two'])
     self.ls.release()
 
+  def testConcurrentSharedSetLock(self):
+    # share the set-lock...
+    self.ls.acquire(None, shared=1)
+    # ...another thread can share it too
+    Thread(target=self._doLockSet, args=(None, 1)).start()
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    # ...or just share some elements
+    Thread(target=self._doLockSet, args=(['one', 'three'], 1)).start()
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    # ...but not add new ones or remove any
+    Thread(target=self._doAddSet, args=(['nine'])).start()
+    Thread(target=self._doRemoveSet, args=(['two'], )).start()
+    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
+    # this just releases the set-lock
+    self.ls.release([])
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    # release the lock on the actual elements so remove() can proceed too
+    self.ls.release()
+    self.assertEqual(self.done.get(True, 1), ['two'])
+
+  def testConcurrentExclusiveSetLock(self):
+    # acquire the set-lock...
+    self.ls.acquire(None, shared=0)
+    # ...no one can do anything else
+    Thread(target=self._doLockSet, args=(None, 1)).start()
+    Thread(target=self._doLockSet, args=(None, 0)).start()
+    Thread(target=self._doLockSet, args=(['three'], 0)).start()
+    Thread(target=self._doLockSet, args=(['two'], 1)).start()
+    Thread(target=self._doAddSet, args=(['nine'])).start()
+    self.ls.release()
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+    self.assertEqual(self.done.get(True, 1), 'DONE')
+
 
 class TestGanetiLockManager(unittest.TestCase):