summaryrefslogtreecommitdiff
path: root/debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch')
-rw-r--r--debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch249
1 files changed, 249 insertions, 0 deletions
diff --git a/debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch b/debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch
new file mode 100644
index 00000000..90711ad1
--- /dev/null
+++ b/debian/patches/0004-fatfs-use-a-seperate-lock-to-protect-nodehash.patch
@@ -0,0 +1,249 @@
+From cc5be07ba485add1691fbfea1ffd4ee05a1442da Mon Sep 17 00:00:00 2001
+From: Justus Winter <4winter@informatik.uni-hamburg.de>
+Date: Tue, 13 May 2014 15:14:53 +0200
+Subject: [PATCH 4/9] fatfs: use a seperate lock to protect nodehash
+
+Previously, fatfs used diskfs_node_refcnt_lock to serialize access to
+the nodehash.
+
+Use a separate lock to protect nodehash. Adjust the reference
+counting accordingly. Every node in the nodehash carries a light
+reference. When we are asked to give up that light reference, we
+reacquire our lock momentarily to check whether someone else
+reacquired a reference through the nodehash.
+
+* fatfs/inode.c (nodecache_lock): New lock.
+(diskfs_cached_lookup): Use a separate lock to protect nodehash.
+Adjust the reference counting accordingly.
+(ifind): Likewise.
+(diskfs_node_iterate): Likewise.
+(diskfs_node_norefs): Move the code removing the node from nodehash...
+(diskfs_try_dropping_softrefs): ... here, where we check whether
+someone reacquired a reference, and if so hold on to our light
+reference.
+---
+ fatfs/inode.c | 95 +++++++++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 66 insertions(+), 29 deletions(-)
+
+diff --git a/fatfs/inode.c b/fatfs/inode.c
+index ed6f3f0..627228c 100644
+--- a/fatfs/inode.c
++++ b/fatfs/inode.c
+@@ -44,8 +44,18 @@
+ #define INOHASH(ino) (((unsigned)(ino))%INOHSZ)
+ #endif
+
++/* The nodehash is a cache of nodes.
++
++ Access to nodehash and nodehash_nr_items is protected by
++ nodecache_lock.
++
++ Every node in the nodehash carries a light reference. When we are
++ asked to give up that light reference, we reacquire our lock
++ momentarily to check whether someone else reacquired a reference
++ through the nodehash. */
+ static struct node *nodehash[INOHSZ];
+ static size_t nodehash_nr_items;
++static pthread_rwlock_t nodecache_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+ static error_t read_node (struct node *np, vm_address_t buf);
+
+@@ -67,24 +77,23 @@ diskfs_cached_lookup (ino64_t inum, struct node **npp)
+ struct node *np;
+ struct disknode *dn;
+
+- pthread_spin_lock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_rdlock (&nodecache_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ if (np->cache_id == inum)
+ {
+- np->references++;
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
++ diskfs_nref (np);
++ pthread_rwlock_unlock (&nodecache_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
++ pthread_rwlock_unlock (&nodecache_lock);
+
+ /* Format specific data for the new node. */
+ dn = malloc (sizeof (struct disknode));
+ if (! dn)
+- {
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
+- return ENOMEM;
+- }
++ return ENOMEM;
++
+ dn->pager = 0;
+ dn->first = 0;
+ dn->last = 0;
+@@ -102,15 +111,16 @@ diskfs_cached_lookup (ino64_t inum, struct node **npp)
+ pthread_mutex_lock (&np->lock);
+
+ /* Put NP in NODEHASH. */
++ pthread_rwlock_wrlock (&nodecache_lock);
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
++ diskfs_nref_light (np);
+ nodehash_nr_items += 1;
++ pthread_rwlock_unlock (&nodecache_lock);
+
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
+-
+ /* Get the contents of NP off disk. */
+ err = read_node (np, 0);
+
+@@ -133,24 +143,23 @@ diskfs_cached_lookup_in_dirbuf (int inum, struct node **npp, vm_address_t buf)
+ struct node *np;
+ struct disknode *dn;
+
+- pthread_spin_lock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_rdlock (&nodecache_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ if (np->cache_id == inum)
+ {
+- np->references++;
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
++ diskfs_nref (np);
++ pthread_rwlock_unlock (&nodecache_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
++ pthread_rwlock_unlock (&nodecache_lock);
+
+ /* Format specific data for the new node. */
+ dn = malloc (sizeof (struct disknode));
+ if (! dn)
+- {
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
+- return ENOMEM;
+- }
++ return ENOMEM;
++
+ dn->pager = 0;
+ dn->first = 0;
+ dn->last = 0;
+@@ -168,15 +177,16 @@ diskfs_cached_lookup_in_dirbuf (int inum, struct node **npp, vm_address_t buf)
+ pthread_mutex_lock (&np->lock);
+
+ /* Put NP in NODEHASH. */
++ pthread_rwlock_wrlock (&nodecache_lock);
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
++ diskfs_nref_light (np);
+ nodehash_nr_items += 1;
++ pthread_rwlock_unlock (&nodecache_lock);
+
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
+-
+ /* Get the contents of NP off disk. */
+ err = read_node (np, buf);
+
+@@ -196,14 +206,13 @@ ifind (ino_t inum)
+ {
+ struct node *np;
+
+- pthread_spin_lock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_rdlock (&nodecache_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->cache_id != inum)
+ continue;
+
+- assert (np->references);
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_unlock (&nodecache_lock);
+ return np;
+ }
+ assert (0);
+@@ -216,11 +225,6 @@ diskfs_node_norefs (struct node *np)
+ {
+ struct cluster_chain *last = np->dn->first;
+
+- *np->dn->hprevp = np->dn->hnext;
+- if (np->dn->hnext)
+- np->dn->hnext->dn->hprevp = np->dn->hprevp;
+- nodehash_nr_items -= 1;
+-
+ while (last)
+ {
+ struct cluster_chain *next = last->next;
+@@ -251,6 +255,35 @@ diskfs_node_norefs (struct node *np)
+ void
+ diskfs_try_dropping_softrefs (struct node *np)
+ {
++ pthread_rwlock_wrlock (&nodecache_lock);
++ if (np->dn->hnext != NULL)
++ {
++ /* Check if someone reacquired a reference through the
++ nodehash. */
++ unsigned int references;
++ pthread_spin_lock (&diskfs_node_refcnt_lock);
++ references = np->references;
++ pthread_spin_unlock (&diskfs_node_refcnt_lock);
++
++ /* An additional reference is acquired by libdiskfs across calls
++ to diskfs_try_dropping_softrefs. */
++ if (references > 1)
++ {
++ /* A reference was reacquired through a hash table lookup.
++ It's fine, we didn't touch anything yet. */
++ pthread_rwlock_unlock (&nodecache_lock);
++ return;
++ }
++
++ *np->dn->hprevp = np->dn->hnext;
++ if (np->dn->hnext)
++ np->dn->hnext->dn->hprevp = np->dn->hprevp;
++ np->dn->hnext = NULL;
++ nodehash_nr_items -= 1;
++ diskfs_nrele_light (np);
++ }
++ pthread_rwlock_unlock (&nodecache_lock);
++
+ drop_pager_softrefs (np);
+ }
+
+@@ -554,12 +587,12 @@ diskfs_node_iterate (error_t (*fun)(struct node *))
+ size_t num_nodes;
+ struct node *node, **node_list, **p;
+
+- pthread_spin_lock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_rdlock (&nodecache_lock);
+
+ /* We must copy everything from the hash table into another data structure
+ to avoid running into any problems with the hash-table being modified
+ during processing (normally we delegate access to hash-table with
+- diskfs_node_refcnt_lock, but we can't hold this while locking the
++ nodecache_lock, but we can't hold this while locking the
+ individual node locks). */
+
+ num_nodes = nodehash_nr_items;
+@@ -570,10 +603,14 @@ diskfs_node_iterate (error_t (*fun)(struct node *))
+ for (node = nodehash[n]; node; node = node->dn->hnext)
+ {
+ *p++ = node;
++
++ /* We acquire a hard reference for node, but without using
++ diskfs_nref. We do this so that diskfs_new_hardrefs will not
++ get called. */
+ node->references++;
+ }
+
+- pthread_spin_unlock (&diskfs_node_refcnt_lock);
++ pthread_rwlock_unlock (&nodecache_lock);
+
+ p = node_list;
+ while (num_nodes-- > 0)
+--
+2.0.0.rc2
+