github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/socket/unix/transport/queue.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package transport
    16  
    17  import (
    18  	"github.com/metacubex/gvisor/pkg/atomicbitops"
    19  	"github.com/metacubex/gvisor/pkg/context"
    20  	"github.com/metacubex/gvisor/pkg/syserr"
    21  	"github.com/metacubex/gvisor/pkg/waiter"
    22  )
    23  
    24  // queue is a buffer queue.
    25  //
    26  // +stateify savable
    27  type queue struct {
    28  	queueRefs
    29  
    30  	ReaderQueue *waiter.Queue
    31  	WriterQueue *waiter.Queue
    32  
    33  	mu       queueMutex `state:"nosave"`
    34  	closed   atomicbitops.Bool
    35  	unread   bool
    36  	used     int64
    37  	limit    int64
    38  	dataList messageList
    39  }
    40  
    41  // Close closes q for reading and writing. It is immediately not writable and
    42  // will become unreadable when no more data is pending.
    43  //
    44  // Both the read and write queues must be notified after closing:
    45  // q.ReaderQueue.Notify(waiter.ReadableEvents)
    46  // q.WriterQueue.Notify(waiter.WritableEvents)
    47  func (q *queue) Close() {
    48  	q.mu.Lock()
    49  	q.closed.Store(true)
    50  	q.mu.Unlock()
    51  }
    52  
    53  func (q *queue) isClosed() bool {
    54  	return q.closed.Load()
    55  }
    56  
    57  // Reset empties the queue and Releases all of the Entries.
    58  //
    59  // Both the read and write queues must be notified after resetting:
    60  // q.ReaderQueue.Notify(waiter.ReadableEvents)
    61  // q.WriterQueue.Notify(waiter.WritableEvents)
    62  func (q *queue) Reset(ctx context.Context) {
    63  	q.mu.Lock()
    64  	dataList := q.dataList
    65  	q.dataList.Reset()
    66  	q.used = 0
    67  	q.mu.Unlock()
    68  
    69  	for cur := dataList.Front(); cur != nil; cur = cur.Next() {
    70  		cur.Release(ctx)
    71  	}
    72  }
    73  
    74  // DecRef implements RefCounter.DecRef.
    75  func (q *queue) DecRef(ctx context.Context) {
    76  	q.queueRefs.DecRef(func() {
    77  		// We don't need to notify after resetting because no one cares about
    78  		// this queue after all references have been dropped.
    79  		q.Reset(ctx)
    80  	})
    81  }
    82  
    83  // IsReadable determines if q is currently readable.
    84  func (q *queue) IsReadable() bool {
    85  	q.mu.Lock()
    86  	defer q.mu.Unlock()
    87  
    88  	return q.closed.RacyLoad() || q.dataList.Front() != nil
    89  }
    90  
    91  // bufWritable returns true if there is space for writing.
    92  //
    93  // N.B. Linux only considers a unix socket "writable" if >75% of the buffer is
    94  // free.
    95  //
    96  // See net/unix/af_unix.c:unix_writeable.
    97  func (q *queue) bufWritable() bool {
    98  	return 4*q.used < q.limit
    99  }
   100  
   101  // IsWritable determines if q is currently writable.
   102  func (q *queue) IsWritable() bool {
   103  	q.mu.Lock()
   104  	defer q.mu.Unlock()
   105  
   106  	return q.closed.RacyLoad() || q.bufWritable()
   107  }
   108  
   109  // Enqueue adds an entry to the data queue if room is available.
   110  //
   111  // If discardEmpty is true and there are zero bytes of data, the packet is
   112  // dropped.
   113  //
   114  // If truncate is true, Enqueue may truncate the message before enqueuing it.
   115  // Otherwise, the entire message must fit. If l is less than the size of data,
   116  // err indicates why.
   117  //
   118  // If notify is true, ReaderQueue.Notify must be called:
   119  // q.ReaderQueue.Notify(waiter.ReadableEvents)
   120  func (q *queue) Enqueue(ctx context.Context, data [][]byte, c ControlMessages, from Address, discardEmpty bool, truncate bool) (l int64, notify bool, err *syserr.Error) {
   121  	q.mu.Lock()
   122  
   123  	if q.closed.RacyLoad() {
   124  		q.mu.Unlock()
   125  		return 0, false, syserr.ErrClosedForSend
   126  	}
   127  
   128  	for _, d := range data {
   129  		l += int64(len(d))
   130  	}
   131  	if discardEmpty && l == 0 {
   132  		q.mu.Unlock()
   133  		c.Release(ctx)
   134  		return 0, false, nil
   135  	}
   136  
   137  	free := q.limit - q.used
   138  
   139  	if l > free && truncate {
   140  		if free <= 0 {
   141  			// Message can't fit right now.
   142  			q.mu.Unlock()
   143  			return 0, false, syserr.ErrWouldBlock
   144  		}
   145  
   146  		l = free
   147  		err = syserr.ErrWouldBlock
   148  	}
   149  
   150  	if l > q.limit {
   151  		// Message is too big to ever fit.
   152  		q.mu.Unlock()
   153  		return 0, false, syserr.ErrMessageTooLong
   154  	}
   155  
   156  	if l > free {
   157  		// Message can't fit right now, and could not be truncated.
   158  		q.mu.Unlock()
   159  		return 0, false, syserr.ErrWouldBlock
   160  	}
   161  
   162  	// Aggregate l bytes of data. This will truncate the data if l is less than
   163  	// the total bytes held in data.
   164  	v := make([]byte, l)
   165  	for i, b := 0, v; i < len(data) && len(b) > 0; i++ {
   166  		n := copy(b, data[i])
   167  		b = b[n:]
   168  	}
   169  
   170  	notify = true
   171  	q.used += l
   172  	q.dataList.PushBack(&message{
   173  		Data:    v,
   174  		Control: c,
   175  		Address: from,
   176  	})
   177  
   178  	q.mu.Unlock()
   179  
   180  	return l, notify, err
   181  }
   182  
   183  // Dequeue removes the first entry in the data queue, if one exists.
   184  //
   185  // If notify is true, WriterQueue.Notify must be called:
   186  // q.WriterQueue.Notify(waiter.WritableEvents)
   187  func (q *queue) Dequeue() (e *message, notify bool, err *syserr.Error) {
   188  	q.mu.Lock()
   189  
   190  	if q.dataList.Front() == nil {
   191  		err := syserr.ErrWouldBlock
   192  		if q.closed.RacyLoad() {
   193  			err = syserr.ErrClosedForReceive
   194  			if q.unread {
   195  				err = syserr.ErrConnectionReset
   196  			}
   197  		}
   198  		q.mu.Unlock()
   199  
   200  		return nil, false, err
   201  	}
   202  
   203  	e = q.dataList.Front()
   204  	q.dataList.Remove(e)
   205  	q.used -= e.Length()
   206  
   207  	notify = q.bufWritable()
   208  
   209  	q.mu.Unlock()
   210  
   211  	return e, notify, nil
   212  }
   213  
   214  // Peek returns the first entry in the data queue, if one exists.
   215  func (q *queue) Peek() (*message, *syserr.Error) {
   216  	q.mu.Lock()
   217  	defer q.mu.Unlock()
   218  
   219  	if q.dataList.Front() == nil {
   220  		err := syserr.ErrWouldBlock
   221  		if q.closed.RacyLoad() {
   222  			if err = syserr.ErrClosedForReceive; q.unread {
   223  				err = syserr.ErrConnectionReset
   224  			}
   225  		}
   226  		return nil, err
   227  	}
   228  
   229  	return q.dataList.Front().Peek(), nil
   230  }
   231  
   232  // QueuedSize returns the number of bytes currently in the queue, that is, the
   233  // number of readable bytes.
   234  func (q *queue) QueuedSize() int64 {
   235  	q.mu.Lock()
   236  	defer q.mu.Unlock()
   237  	return q.used
   238  }
   239  
   240  // MaxQueueSize returns the maximum number of bytes storable in the queue.
   241  func (q *queue) MaxQueueSize() int64 {
   242  	q.mu.Lock()
   243  	defer q.mu.Unlock()
   244  	return q.limit
   245  }
   246  
   247  // SetMaxQueueSize sets the maximum number of bytes storable in the queue.
   248  func (q *queue) SetMaxQueueSize(v int64) {
   249  	q.mu.Lock()
   250  	defer q.mu.Unlock()
   251  	q.limit = v
   252  }
   253  
   254  // CloseUnread sets flag to indicate that the peer is closed (not shutdown)
   255  // with unread data. So if read on this queue shall return ECONNRESET error.
   256  func (q *queue) CloseUnread() {
   257  	q.mu.Lock()
   258  	defer q.mu.Unlock()
   259  	q.unread = true
   260  }