summaryrefslogtreecommitdiff
path: root/debian/patches/flavio0001-netfs-Remove-global-reference-count-lock.patch
blob: 5561bdd910b66923d4721615748cc4904062aab1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
From 5eef605eb523e4148ccd22578327492178cfd0c4 Mon Sep 17 00:00:00 2001
From: Flavio Cruz <flaviocruz@gmail.com>
Date: Tue, 15 Mar 2016 04:50:02 -0400
Subject: [PATCH hurd] netfs: Remove global reference count lock.

* libnetfs/drop-node.c: Remove use of netfs_node_refcnt_lock.
* libnetfs/init-init.c: Remove netfs_node_refcnt_lock.
* libnetfs/make-node.c: Initialize refcounts in refcounts_init.
* libnetfs/netfs.h: Use refcounts_t for tracking node references. Remove
netfs_node_refcnt_lock. Add netfs_nref_light, netfs_nrele_light and
handler netfs_try_dropping_softrefs. Adjust comments.
* libnetfs/nput.c: Use refcounts_t. Call netfs_try_dropping_softrefs to
remove any soft reference that the translator might have acquired
during the lifetime of the node. Implement empty
netfs_try_dropping_softrefs.
* libnetfs/nref.c: Implement netfs_nref_light.
* libnetfs/nrele.c: Use refcounts_t and netfs_try_dropping_softrefs.
Implement netfs_nrele_light.
* ftpfs/dir.c: Use netfs_nref without locking the old
netfs_node_refcnt_lock.
* ftpfs/node.c: Likewise.
* usermux/mux.c: Use netfs_nref to increase hard references of the node.
* hostmux/mux.c: Use netfs_nref to increase hard references of the node.
* trans/fakeroot.c (new_node): Use a light reference when storing a node in the
hash table.
* trans/fakeroot.c (netfs_try_dropping_softrefs): Implement
netfs_try_dropping_softrefs to remove the node from the hash table.
* trans/fakeroot.c (netfs_node_norefs): Remove code to remove the node
from the hash table.
* trans/fakeroot.c (netfs_S_dir_lookup): Simplify lookup code since we
don't need to lock netfs_node_refcnt_lock anymore.
* procfs/netfs.c: Remove use of netfs_node_refcnt_lock.
* nfs/cache.c: Add mutex to handle exclusive access to nodehash. This
replaces the use of netfs_node_refcnt_lock.
* nfs/cache.c (lookup_handle): Use nodehash_ihash_lock when accessing
nodehash. Use netfs_nref_light to add one soft reference to the node
just added to nodehash.
* nfs/cache.c (netfs_node_norefs): Use netfs_nref. Don't use
netfs_node_refcnt_lock and don't remove the node from nodehash here.
* nfs/cache.c (netfs_try_dropping_softrefs): Drop the light reference
when the node has no more hard references.
* nfs/cache.c (recache_handle): Use nodehash_ihash_lock instead.
* nfs/ops.c (netds_attempt_unlink): Use refcounts_references.
* console/console.c (netfs_node_norefs): Use a soft reference to store
a node in dir_node, cons_node, disp_node, inp_node.
* console/console.c (netfs_try_dropping_softrefs): When dropping all
soft references remove node pointer from the fields above.
---
 console/console.c    | 64 ++++++++++++++++++++++++++++++----------------------
 ftpfs/dir.c          | 27 +++++-----------------
 ftpfs/node.c         |  8 +------
 hostmux/mux.c        |  6 ++---
 libnetfs/drop-node.c |  1 -
 libnetfs/init-init.c |  2 --
 libnetfs/make-node.c |  2 +-
 libnetfs/netfs.h     | 42 ++++++++++++++++++++++------------
 libnetfs/nput.c      | 27 ++++++++++++++--------
 libnetfs/nref.c      | 10 +++++---
 libnetfs/nrele.c     | 37 ++++++++++++++++++++++++------
 nfs/cache.c          | 40 ++++++++++++++++++--------------
 nfs/ops.c            |  5 +++-
 procfs/netfs.c       |  4 ----
 trans/fakeroot.c     | 45 +++++++++++++++++++++---------------
 usermux/mux.c        |  6 ++---
 16 files changed, 185 insertions(+), 141 deletions(-)

diff --git a/console/console.c b/console/console.c
index 57ae813..9c5869d 100644
--- a/console/console.c
+++ b/console/console.c
@@ -415,47 +415,51 @@ new_node (struct node **np, vcons_t vcons, vcons_node_type type)
 
 /* Node management.  */
 
-/* Node NP has no more references; free all its associated
-   storage.  */
+/* We need to drop the soft references on NP.  */
 void
-netfs_node_norefs (struct node *np)
+netfs_try_dropping_softrefs (struct node *np)
 {
   vcons_t vcons = np->nn->vcons;
+  int release = FALSE;
 
-  /* The root node does never go away.  */
-  assert (!np->nn->cons && np->nn->vcons);
-
-  /* Avoid deadlock.  */
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
-
-  /* Find the back reference to ourself in the virtual console
-     structure, and delete it.  */
   pthread_mutex_lock (&vcons->lock);
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  if (np->references)
+  if (np == vcons->dir_node)
     {
-      /* Someone else got a reference while we were attempting to go
-	 away.  This can happen in netfs_attempt_lookup.  In this
-	 case, just unlock the node and do nothing else.  */
-      pthread_mutex_unlock (&vcons->lock);
-      pthread_mutex_unlock (&np->lock);
-      return;
+      release = TRUE;
+      vcons->dir_node = 0;
     }
-  if (np == vcons->dir_node)
-    vcons->dir_node = 0;
   else if (np == vcons->cons_node)
-    vcons->cons_node = 0;
+    {
+      release = TRUE;
+      vcons->cons_node = 0;
+    }
   else if (np == vcons->disp_node)
-    vcons->disp_node = 0;
-  else
     {
-      assert (np == vcons->inpt_node);
+      release = TRUE;
+      vcons->disp_node = 0;
+    }
+  else if (np == vcons->inpt_node)
+    {
+      release = TRUE;
       vcons->inpt_node = 0;
     }
+  if (release)
+    netfs_nrele_light (np);
   pthread_mutex_unlock (&vcons->lock);
 
   /* Release our reference.  */
-  vcons_release (vcons);
+  if (release)
+    vcons_release (vcons);
+
+}
+
+/* Node NP has no more references; free all its associated
+   storage.  */
+void
+netfs_node_norefs (struct node *np)
+{
+  /* The root node does never go away.  */
+  assert (!np->nn->cons && np->nn->vcons);
 
   free (np->nn);
   free (np);
@@ -634,7 +638,10 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
 	     the virtual console.  */
 	  err = new_node (node, vcons, VCONS_NODE_DIR);
 	  if (!err)
-	    vcons->dir_node = *node;
+            {
+              vcons->dir_node = *node;
+              netfs_nref_light (*node);
+            }
 	  else
 	    release_vcons = 1;
 	}
@@ -663,6 +670,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
 	      if (!err)
 		{
 		  vcons->cons_node = *node;
+                  netfs_nref_light (*node);
 		  ref_vcons = 1;
 		}
 	    }
@@ -682,6 +690,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
 	      if (!err)
 		{
 		  vcons->disp_node = *node;
+                  netfs_nref_light (*node);
 		  ref_vcons = 1;
 		}
 	    }
@@ -701,6 +710,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
 	      if (!err)
 		{
 		  vcons->inpt_node = *node;
+                  netfs_nref_light (*node);
 		  ref_vcons = 1;
 		}
 	    }
diff --git a/ftpfs/dir.c b/ftpfs/dir.c
index 733a2dc..2ea29b5 100644
--- a/ftpfs/dir.c
+++ b/ftpfs/dir.c
@@ -654,10 +654,8 @@ ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
 	{
 	  /* If there's already a node, add a ref so that it doesn't go
              away.  */
-	  pthread_spin_lock (&netfs_node_refcnt_lock);
-	  if (e->node)
-	    e->node->references++;
-	  pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (e->node)
+            netfs_nref (e->node);
 
 	  if (! e->node)
 	    /* No node; make one and install it into E.  */
@@ -682,11 +680,7 @@ ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
 		  if (!err && dir->num_live_entries++ == 0)
 		    /* Keep a reference to dir's node corresponding to
 		       children.  */
-		    {
-		      pthread_spin_lock (&netfs_node_refcnt_lock);
-		      dir->node->references++;
-		      pthread_spin_unlock (&netfs_node_refcnt_lock);
-		    }
+                    netfs_nref (dir->node);
 		}
 	    }
 
@@ -737,10 +731,8 @@ ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node)
     /* We've got a dir entry, get a node for it.  */
     {
       /* If there's already a node, add a ref so that it doesn't go away.  */
-      pthread_spin_lock (&netfs_node_refcnt_lock);
       if (e->node)
-	e->node->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+        netfs_nref (e->node);
 
       if (! e->node)
 	/* No node; make one and install it into E.  */
@@ -749,11 +741,7 @@ ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node)
 
 	  if (!err && dir->num_live_entries++ == 0)
 	    /* Keep a reference to dir's node corresponding to children.  */
-	    {
-	      pthread_spin_lock (&netfs_node_refcnt_lock);
-	      dir->node->references++;
-	      pthread_spin_unlock (&netfs_node_refcnt_lock);
-	    }
+            netfs_nref (dir->node);
 	}
 
       if (! err)
@@ -783,10 +771,7 @@ ftpfs_dir_create (struct ftpfs *fs, struct node *node, const char *rmt_path,
       return ENOMEM;
     }
 
-  /* Hold a reference to the new dir's node.  */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  node->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  netfs_nref (node);
 
   hurd_ihash_init (&new->htable, offsetof (struct ftpfs_dir_entry, dir_locp));
   hurd_ihash_set_gki (&new->htable, ihash_hash, ihash_compare);
diff --git a/ftpfs/node.c b/ftpfs/node.c
index 74cd402..cc9bf43 100644
--- a/ftpfs/node.c
+++ b/ftpfs/node.c
@@ -84,10 +84,7 @@ netfs_node_norefs (struct node *node)
 {
   struct netnode *nn = node->nn;
 
-  /* Ftpfs_detach_node does ref count frobbing (of other nodes), so we have
-     to unlock NETFS_NODE_REFCNT_LOCK during it.  */
-  node->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  netfs_nref (node);
 
   /* Remove NODE from any entry it is attached to.  */
   ftpfs_detach_node (node);
@@ -108,7 +105,4 @@ netfs_node_norefs (struct node *node)
 
   free (nn);
   free (node);
-
-  /* Caller expects us to leave this locked... */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
diff --git a/hostmux/mux.c b/hostmux/mux.c
index 81d3961..ddca89d 100644
--- a/hostmux/mux.c
+++ b/hostmux/mux.c
@@ -240,10 +240,8 @@ lookup_cached (struct hostmux *mux, const char *host, int purge,
 
       if (strcasecmp (host, nm->name) == 0)
 	{
-	  pthread_spin_lock (&netfs_node_refcnt_lock);
-	  if (nm->node)
-	    nm->node->references++;
-	  pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (nm->node)
+            netfs_nref (nm->node);
 
 	  if (nm->node)
 	    {
diff --git a/libnetfs/drop-node.c b/libnetfs/drop-node.c
index 2fe5ce9..f0d69be 100644
--- a/libnetfs/drop-node.c
+++ b/libnetfs/drop-node.c
@@ -25,5 +25,4 @@ netfs_drop_node (struct node *np)
 {
   fshelp_drop_transbox (&np->transbox);
   netfs_node_norefs (np);
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
 }
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index a088ad5..9ca1aac 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -24,8 +24,6 @@
 /* For safe inlining of netfs_node_netnode and netfs_netnode_node.  */
 size_t const _netfs_sizeof_struct_node = sizeof (struct node);
 
-pthread_spinlock_t netfs_node_refcnt_lock = PTHREAD_SPINLOCK_INITIALIZER;
-
 struct node *netfs_root_node = 0;
 struct port_bucket *netfs_port_bucket = 0;
 struct port_class *netfs_protid_class = 0;
diff --git a/libnetfs/make-node.c b/libnetfs/make-node.c
index 6bd8109..a292dc6 100644
--- a/libnetfs/make-node.c
+++ b/libnetfs/make-node.c
@@ -27,7 +27,7 @@ init_node (struct node *np, struct netnode *nn)
   np->nn = nn;
 
   pthread_mutex_init (&np->lock, NULL);
-  np->references = 1;
+  refcounts_init (&np->refcounts, 1, 0);
   np->sockaddr = MACH_PORT_NULL;
   np->owner = 0;
 
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index 67a6a9a..6c989a4 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -82,8 +82,8 @@ struct node
 
   pthread_mutex_t lock;
 
-  /* The number of references to this node.  */
-  int references;
+  /* Hard and soft references to this node.  */
+  refcounts_t refcounts;
 
   mach_port_t sockaddr;
 
@@ -397,10 +397,6 @@ netfs_netnode_node (struct netnode *netnode)
   return (struct node *) ((char *) netnode - _netfs_sizeof_struct_node);
 }
 
-/* Whenever node->references is to be touched, this lock must be
-   held.  Cf. netfs_nrele, netfs_nput, netfs_nref and netfs_drop_node.  */
-extern pthread_spinlock_t netfs_node_refcnt_lock;
-
 /* Normally called in main.  This function sets up some of the netfs
    server's internal state.  */
 void netfs_init (void);
@@ -425,22 +421,38 @@ struct protid *netfs_make_protid (struct peropen *po, struct iouser *user);
 struct peropen *netfs_make_peropen (struct node *, int,
 				    struct peropen *context);
 
-/* Add a reference to node NP. Unless you already hold a reference,
+/* Add a hard reference to node NP. Unless you already hold a reference,
    NP must be locked.  */
 void netfs_nref (struct node *np);
 
-/* Releases a node.  Drops a reference to node NP, which must not be
-   locked by the caller.  If this was the last reference, drops the
-   node.  The node cannot be used again without first obtaining a
-   reference to it.  */
+/* Add a light reference to a node.  */
+void netfs_nref_light (struct node *np);
+
+/* Releases a hard reference on NP. If NP is locked by anyone, then
+   this cannot be the last hard reference (because you must hold a
+   hard reference in order to hold the lock). If this is the last
+   hard reference then request soft references to be dropped.  */
 void netfs_nrele (struct node *np);
 
-/* Puts a node back.  Drops a reference to the node NP, which must be
-   locked by the caller (this lock will be released by netfs_nput).
-   If this was the last reference, drops the node.  The node cannot be
-   used again without first obtaining a reference to it.  */
+/* Release a soft reference on NP. If NP is locked by anyone, then
+   this cannot be the last reference (because you must hold a hard
+   reference in order to hold the lock).  */
+void netfs_nrele_light (struct node *np);
+
+/* Puts a node back by releasing a hard reference on NP, which must
+   be locked by the caller (this lock will be released by netfs_nput).
+   If this was the last reference, then request soft references to be
+   dropped.  */
 void netfs_nput (struct node *np);
 
+/* The user must define this function in order to drop the soft references
+   that this node may have. When this function is called, node NP has just
+   lost its hard references and is now trying to also drop its soft references.
+   If the node is stored in another data structure (for caching purposes),
+   this allows the user to remove it so that the node can be safely deleted
+   from memory.  */
+void netfs_try_dropping_softrefs (struct node *np);
+
 /* Called internally when no more references to node NP exist. */
 void netfs_drop_node (struct node *np);
 
diff --git a/libnetfs/nput.c b/libnetfs/nput.c
index 522c714..b04fc4b 100644
--- a/libnetfs/nput.c
+++ b/libnetfs/nput.c
@@ -23,15 +23,24 @@
 void
 netfs_nput (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references == 0)
+  struct references result;
+
+  refcounts_demote (&np->refcounts, &result);
+
+  if (result.hard == 0)
+    netfs_try_dropping_softrefs (np);
+
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
     netfs_drop_node (np);
-    /* netfs_drop_node drops netfs_node_refcnt_lock for us.  */
   else
-    {
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
-      pthread_mutex_unlock (&np->lock);
-    }
+    pthread_mutex_unlock (&np->lock);
+}
+
+/* The last hard reference to NP has gone away; the user must define
+   this function in order to drop all the soft references.  */
+void __attribute__ ((weak))
+netfs_try_dropping_softrefs (struct node *np)
+{
 }
diff --git a/libnetfs/nref.c b/libnetfs/nref.c
index 86b4992..a40cf4d 100644
--- a/libnetfs/nref.c
+++ b/libnetfs/nref.c
@@ -23,7 +23,11 @@
 void
 netfs_nref (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  np->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  refcounts_ref (&np->refcounts, NULL);
+}
+
+void
+netfs_nref_light (struct node *np)
+{
+  refcounts_ref_weak (&np->refcounts, NULL);
 }
diff --git a/libnetfs/nrele.c b/libnetfs/nrele.c
index 6f9a014..4dddd1f 100644
--- a/libnetfs/nrele.c
+++ b/libnetfs/nrele.c
@@ -23,15 +23,38 @@
 void
 netfs_nrele (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references == 0)
+  struct references result;
+  int locked = FALSE;
+
+  refcounts_demote (&np->refcounts, &result);
+
+  if (result.hard == 0)
+    {
+      pthread_mutex_lock (&np->lock);
+      netfs_try_dropping_softrefs (np);
+      locked = TRUE;
+    }
+
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
+    {
+      if (! locked)
+        pthread_mutex_lock (&np->lock);
+      netfs_drop_node (np);
+    } else if (locked)
+      pthread_mutex_unlock (&np->lock);
+}
+
+void
+netfs_nrele_light (struct node *np)
+{
+  struct references result;
+
+  refcounts_deref_weak (&np->refcounts, &result);
+  if (result.hard == 0 && result.weak == 0)
     {
       pthread_mutex_lock (&np->lock);
       netfs_drop_node (np);
-      /* netfs_drop_node drops netfs_node_refcnt_lock for us.  */
     }
-  else
-    pthread_spin_unlock (&netfs_node_refcnt_lock);
 }
diff --git a/nfs/cache.c b/nfs/cache.c
index b48152e..2015603 100644
--- a/nfs/cache.c
+++ b/nfs/cache.c
@@ -49,6 +49,8 @@ static struct hurd_ihash nodehash =
                               + offsetof (struct netnode, slot), NULL, NULL,
                               ihash_hash, ihash_compare);
 
+pthread_mutex_t nodehash_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /* Lookup the file handle HANDLE in the hash table.  If it is
    not present, initialize a new node structure and insert it into the
    hash table.  Whichever course, a new reference is generated and the
@@ -60,12 +62,12 @@ lookup_fhandle (struct fhandle *handle, struct node **npp)
   struct node *np;
   struct netnode *nn;
 
-  pthread_spin_lock (&netfs_node_refcnt_lock);
+  pthread_mutex_lock (&nodehash_ihash_lock);
   np = hurd_ihash_find (&nodehash, (hurd_ihash_key_t) handle);
   if (np)
     {
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
+      pthread_mutex_unlock (&nodehash_ihash_lock);
       pthread_mutex_lock (&np->lock);
       *npp = np;
       return;
@@ -84,9 +86,9 @@ lookup_fhandle (struct fhandle *handle, struct node **npp)
   nn->dead_name = 0;
   
   hurd_ihash_add (&nodehash, (hurd_ihash_key_t) &nn->handle, np);
+  netfs_nref_light (np);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
   pthread_mutex_lock (&np->lock);
-
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
   
   *npp = np;
 }
@@ -114,9 +116,7 @@ forked_node_delete (void *arg)
 };
 
 /* Called by libnetfs when node NP has no more references.  (See
-   <hurd/libnetfs.h> for details.  Just clear its local state and
-   remove it from the hash table.  Called and expected to leave with
-   NETFS_NODE_REFCNT_LOCK held.  */
+   <hurd/libnetfs.h> for details.  */
 void
 netfs_node_norefs (struct node *np)
 {
@@ -129,8 +129,7 @@ netfs_node_norefs (struct node *np)
       args = malloc (sizeof (struct fnd));
       assert (args);
 
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
 
       args->dir = np->nn->dead_dir;
       args->name = np->nn->dead_name;
@@ -149,19 +148,26 @@ netfs_node_norefs (struct node *np)
 	  errno = err;
 	  perror ("pthread_create");
 	}
-
-      /* Caller expects us to leave this locked... */
-      pthread_spin_lock (&netfs_node_refcnt_lock);
     }
   else
     {
-      hurd_ihash_locp_remove (&nodehash, np->nn->slot);
       if (np->nn->dtrans == SYMLINK)
-	free (np->nn->transarg.name);
+        free (np->nn->transarg.name);
       free (np);
     }
 }
 
+/* When dropping soft refs, we simply remove the node from the
+   node cache.  */
+void
+netfs_try_dropping_softrefs (struct node *np)
+{
+  pthread_mutex_lock (&nodehash_ihash_lock);
+  hurd_ihash_locp_remove (&nodehash, np->nn->slot);
+  netfs_nrele_light (np);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
+}
+
 /* Change the file handle used for node NP to be the handle at P.
    Make sure the hash table stays up to date.  Return the address
    after the handle.  The lock on the node should be held.  */
@@ -179,7 +185,7 @@ recache_handle (int *p, struct node *np)
     }
   
   /* Unlink it */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
+  pthread_mutex_lock (&nodehash_ihash_lock);
   hurd_ihash_locp_remove (&nodehash, np->nn->slot);
 
   /* Change the name */
@@ -189,6 +195,6 @@ recache_handle (int *p, struct node *np)
   /* Reinsert it */
   hurd_ihash_add (&nodehash, (hurd_ihash_key_t) &np->nn->handle, np);
   
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
   return p + len / sizeof (int);
 }
diff --git a/nfs/ops.c b/nfs/ops.c
index 79cd3a6..33ab38b 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -1267,7 +1267,10 @@ netfs_attempt_unlink (struct iouser *cred, struct node *dir,
      one we just got; if so, we must give this file another link
      so that when we delete the one we are asked for it doesn't go
      away entirely. */
-  if (np->references > 1)
+  struct references result;
+  refcounts_references (&np->refcounts, &result);
+
+  if (result.hard > 1)
     {
       char *newname = 0;
       int n = 0;
diff --git a/procfs/netfs.c b/procfs/netfs.c
index 276c57c..0b3d31a 100644
--- a/procfs/netfs.c
+++ b/procfs/netfs.c
@@ -222,12 +222,8 @@ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
    free all its associated storage. */
 void netfs_node_norefs (struct node *np)
 {
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
-
   procfs_cleanup (np);
   free (np);
-
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
 
 /* The user may define this function (but should define it together
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index cb4f818..ad7bec9 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -108,6 +108,9 @@ new_node (file_t file, mach_port_t idport, int locked, int openmodes,
     }
   nn->faked = FAKE_DEFAULT;
 
+  /* The light reference allows us to safely keep the node in the
+     hash table.  */
+  netfs_nref_light (*np);
   if (!locked)
     pthread_mutex_lock (&idport_ihash_lock);
   err = hurd_ihash_add (&idport_ihash, nn->idport, *np);
@@ -155,22 +158,31 @@ set_faked_attribute (struct node *np, unsigned int faked)
     }
 }
 
+void
+netfs_try_dropping_softrefs (struct node *np)
+{
+  /* We have to drop our light reference by removing the node from the
+     idport_ihash hash table.  */
+  pthread_mutex_lock (&idport_ihash_lock);
+
+  hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
+  pthread_mutex_unlock (&idport_ihash_lock);
+
+  netfs_nrele_light (np);
+}
+
 /* Node NP has no more references; free all its associated storage. */
 void
 netfs_node_norefs (struct node *np)
 {
   pthread_mutex_unlock (&np->lock);
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
 
-  pthread_mutex_lock (&idport_ihash_lock);
-  hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
-  pthread_mutex_unlock (&idport_ihash_lock);
+  /* NP was already removed from idport_ihash through
+     netfs_try_dropping_softrefs.  */
 
   mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->file);
   mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->idport);
   free (np);
-
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
 
 /* This is the cleanup function we install in netfs_protid_class.  If
@@ -363,29 +375,27 @@ netfs_S_dir_lookup (struct protid *diruser,
  redo_hash_lookup:
   pthread_mutex_lock (&idport_ihash_lock);
   pthread_mutex_lock (&dnp->lock);
-  /* The hashtable may not hold a true reference on the node.  Acquire the
-     refcount lock so that, if a node is found, its reference counter cannot
-     drop to 0 before we get our own reference.  */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
   np = hurd_ihash_find (&idport_ihash, idport);
   if (np != NULL)
     {
-      /* We already know about this node.  */
+      /* We quickly check that NP has hard references. If the node is being
+         removed, netfs_try_dropping_softrefs is attempting to drop the light
+         reference on this.  */
+      struct references result;
+
+      refcounts_references (&np->refcounts, &result);
 
-      if (np->references == 0)
+      if (result.hard == 0)
 	{
-	  /* But it might be in the process of being released.  If so,
-	     unlock the hash table to give the node a chance to actually
+	  /* If so, unlock the hash table to give the node a chance to actually
 	     be removed and retry.  */
-	  pthread_spin_unlock (&netfs_node_refcnt_lock);
 	  pthread_mutex_unlock (&dnp->lock);
 	  pthread_mutex_unlock (&idport_ihash_lock);
 	  goto redo_hash_lookup;
 	}
 
       /* Otherwise, reference it right away.  */
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
 
       mach_port_deallocate (mach_task_self (), idport);
 
@@ -405,7 +415,6 @@ netfs_S_dir_lookup (struct protid *diruser,
     }
   else
     {
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
       err = new_node (file, idport, 1, flags & (O_RDWR|O_EXEC), &np);
       pthread_mutex_unlock (&dnp->lock);
       if (!err)
diff --git a/usermux/mux.c b/usermux/mux.c
index bfa95fd..7c57f94 100644
--- a/usermux/mux.c
+++ b/usermux/mux.c
@@ -298,10 +298,8 @@ lookup_cached (struct usermux *mux, const char *user, int purge,
 
       if (strcasecmp (user, nm->name) == 0)
 	{
-	  pthread_spin_lock (&netfs_node_refcnt_lock);
-	  if (nm->node)
-	    nm->node->references++;
-	  pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (nm->node)
+            netfs_nref (nm->node);
 
 	  if (nm->node)
 	    {
-- 
2.1.4