github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/tcpip/link/tun/tun_endpoint_refs.go (about)

     1  package tun
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/MerlinKodo/gvisor/pkg/atomicbitops"
     7  	"github.com/MerlinKodo/gvisor/pkg/refs"
     8  )
     9  
    10  // enableLogging indicates whether reference-related events should be logged (with
    11  // stack traces). This is false by default and should only be set to true for
    12  // debugging purposes, as it can generate an extremely large amount of output
    13  // and drastically degrade performance.
    14  const tunEndpointenableLogging = false
    15  
    16  // obj is used to customize logging. Note that we use a pointer to T so that
    17  // we do not copy the entire object when passed as a format parameter.
    18  var tunEndpointobj *tunEndpoint
    19  
    20  // Refs implements refs.RefCounter. It keeps a reference count using atomic
    21  // operations and calls the destructor when the count reaches zero.
    22  //
    23  // NOTE: Do not introduce additional fields to the Refs struct. It is used by
    24  // many filesystem objects, and we want to keep it as small as possible (i.e.,
    25  // the same size as using an int64 directly) to avoid taking up extra cache
    26  // space. In general, this template should not be extended at the cost of
    27  // performance. If it does not offer enough flexibility for a particular object
    28  // (example: b/187877947), we should implement the RefCounter/CheckedObject
    29  // interfaces manually.
    30  //
    31  // +stateify savable
    32  type tunEndpointRefs struct {
    33  	// refCount is composed of two fields:
    34  	//
    35  	//	[32-bit speculative references]:[32-bit real references]
    36  	//
    37  	// Speculative references are used for TryIncRef, to avoid a CompareAndSwap
    38  	// loop. See IncRef, DecRef and TryIncRef for details of how these fields are
    39  	// used.
    40  	refCount atomicbitops.Int64
    41  }
    42  
    43  // InitRefs initializes r with one reference and, if enabled, activates leak
    44  // checking.
    45  func (r *tunEndpointRefs) InitRefs() {
    46  
    47  	r.refCount.RacyStore(1)
    48  	refs.Register(r)
    49  }
    50  
    51  // RefType implements refs.CheckedObject.RefType.
    52  func (r *tunEndpointRefs) RefType() string {
    53  	return fmt.Sprintf("%T", tunEndpointobj)[1:]
    54  }
    55  
    56  // LeakMessage implements refs.CheckedObject.LeakMessage.
    57  func (r *tunEndpointRefs) LeakMessage() string {
    58  	return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs())
    59  }
    60  
    61  // LogRefs implements refs.CheckedObject.LogRefs.
    62  func (r *tunEndpointRefs) LogRefs() bool {
    63  	return tunEndpointenableLogging
    64  }
    65  
    66  // ReadRefs returns the current number of references. The returned count is
    67  // inherently racy and is unsafe to use without external synchronization.
    68  func (r *tunEndpointRefs) ReadRefs() int64 {
    69  	return r.refCount.Load()
    70  }
    71  
    72  // IncRef implements refs.RefCounter.IncRef.
    73  //
    74  //go:nosplit
    75  func (r *tunEndpointRefs) IncRef() {
    76  	v := r.refCount.Add(1)
    77  	if tunEndpointenableLogging {
    78  		refs.LogIncRef(r, v)
    79  	}
    80  	if v <= 1 {
    81  		panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType()))
    82  	}
    83  }
    84  
    85  // TryIncRef implements refs.TryRefCounter.TryIncRef.
    86  //
    87  // To do this safely without a loop, a speculative reference is first acquired
    88  // on the object. This allows multiple concurrent TryIncRef calls to distinguish
    89  // other TryIncRef calls from genuine references held.
    90  //
    91  //go:nosplit
    92  func (r *tunEndpointRefs) TryIncRef() bool {
    93  	const speculativeRef = 1 << 32
    94  	if v := r.refCount.Add(speculativeRef); int32(v) == 0 {
    95  
    96  		r.refCount.Add(-speculativeRef)
    97  		return false
    98  	}
    99  
   100  	v := r.refCount.Add(-speculativeRef + 1)
   101  	if tunEndpointenableLogging {
   102  		refs.LogTryIncRef(r, v)
   103  	}
   104  	return true
   105  }
   106  
   107  // DecRef implements refs.RefCounter.DecRef.
   108  //
   109  // Note that speculative references are counted here. Since they were added
   110  // prior to real references reaching zero, they will successfully convert to
   111  // real references. In other words, we see speculative references only in the
   112  // following case:
   113  //
   114  //	A: TryIncRef [speculative increase => sees non-negative references]
   115  //	B: DecRef [real decrease]
   116  //	A: TryIncRef [transform speculative to real]
   117  //
   118  //go:nosplit
   119  func (r *tunEndpointRefs) DecRef(destroy func()) {
   120  	v := r.refCount.Add(-1)
   121  	if tunEndpointenableLogging {
   122  		refs.LogDecRef(r, v)
   123  	}
   124  	switch {
   125  	case v < 0:
   126  		panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType()))
   127  
   128  	case v == 0:
   129  		refs.Unregister(r)
   130  
   131  		if destroy != nil {
   132  			destroy()
   133  		}
   134  	}
   135  }
   136  
   137  func (r *tunEndpointRefs) afterLoad() {
   138  	if r.ReadRefs() > 0 {
   139  		refs.Register(r)
   140  	}
   141  }