github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/epoll.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 syscalls
    16  
    17  import (
    18  	"time"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/epoll"
    24  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    25  	"github.com/SagerNet/gvisor/pkg/waiter"
    26  )
    27  
    28  // CreateEpoll implements the epoll_create(2) linux syscall.
    29  func CreateEpoll(t *kernel.Task, closeOnExec bool) (int32, error) {
    30  	file := epoll.NewEventPoll(t)
    31  	defer file.DecRef(t)
    32  
    33  	fd, err := t.NewFDFrom(0, file, kernel.FDFlags{
    34  		CloseOnExec: closeOnExec,
    35  	})
    36  	if err != nil {
    37  		return 0, err
    38  	}
    39  
    40  	return fd, nil
    41  }
    42  
    43  // AddEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_ADD.
    44  func AddEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error {
    45  	// Get epoll from the file descriptor.
    46  	epollfile := t.GetFile(epfd)
    47  	if epollfile == nil {
    48  		return linuxerr.EBADF
    49  	}
    50  	defer epollfile.DecRef(t)
    51  
    52  	// Get the target file id.
    53  	file := t.GetFile(fd)
    54  	if file == nil {
    55  		return linuxerr.EBADF
    56  	}
    57  	defer file.DecRef(t)
    58  
    59  	// Extract the epollPoll operations.
    60  	e, ok := epollfile.FileOperations.(*epoll.EventPoll)
    61  	if !ok {
    62  		return linuxerr.EBADF
    63  	}
    64  
    65  	// Try to add the entry.
    66  	return e.AddEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData)
    67  }
    68  
    69  // UpdateEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_MOD.
    70  func UpdateEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error {
    71  	// Get epoll from the file descriptor.
    72  	epollfile := t.GetFile(epfd)
    73  	if epollfile == nil {
    74  		return linuxerr.EBADF
    75  	}
    76  	defer epollfile.DecRef(t)
    77  
    78  	// Get the target file id.
    79  	file := t.GetFile(fd)
    80  	if file == nil {
    81  		return linuxerr.EBADF
    82  	}
    83  	defer file.DecRef(t)
    84  
    85  	// Extract the epollPoll operations.
    86  	e, ok := epollfile.FileOperations.(*epoll.EventPoll)
    87  	if !ok {
    88  		return linuxerr.EBADF
    89  	}
    90  
    91  	// Try to update the entry.
    92  	return e.UpdateEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData)
    93  }
    94  
    95  // RemoveEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_DEL.
    96  func RemoveEpoll(t *kernel.Task, epfd int32, fd int32) error {
    97  	// Get epoll from the file descriptor.
    98  	epollfile := t.GetFile(epfd)
    99  	if epollfile == nil {
   100  		return linuxerr.EBADF
   101  	}
   102  	defer epollfile.DecRef(t)
   103  
   104  	// Get the target file id.
   105  	file := t.GetFile(fd)
   106  	if file == nil {
   107  		return linuxerr.EBADF
   108  	}
   109  	defer file.DecRef(t)
   110  
   111  	// Extract the epollPoll operations.
   112  	e, ok := epollfile.FileOperations.(*epoll.EventPoll)
   113  	if !ok {
   114  		return linuxerr.EBADF
   115  	}
   116  
   117  	// Try to remove the entry.
   118  	return e.RemoveEntry(t, epoll.FileIdentifier{file, fd})
   119  }
   120  
   121  // WaitEpoll implements the epoll_wait(2) linux syscall.
   122  func WaitEpoll(t *kernel.Task, fd int32, max int, timeoutInNanos int64) ([]linux.EpollEvent, error) {
   123  	// Get epoll from the file descriptor.
   124  	epollfile := t.GetFile(fd)
   125  	if epollfile == nil {
   126  		return nil, linuxerr.EBADF
   127  	}
   128  	defer epollfile.DecRef(t)
   129  
   130  	// Extract the epollPoll operations.
   131  	e, ok := epollfile.FileOperations.(*epoll.EventPoll)
   132  	if !ok {
   133  		return nil, linuxerr.EBADF
   134  	}
   135  
   136  	// Try to read events and return right away if we got them or if the
   137  	// caller requested a non-blocking "wait".
   138  	r := e.ReadEvents(max)
   139  	if len(r) != 0 || timeoutInNanos == 0 {
   140  		return r, nil
   141  	}
   142  
   143  	// We'll have to wait. Set up the timer if a timeout was specified and
   144  	// and register with the epoll object for readability events.
   145  	var haveDeadline bool
   146  	var deadline ktime.Time
   147  	if timeoutInNanos > 0 {
   148  		timeoutDur := time.Duration(timeoutInNanos) * time.Nanosecond
   149  		deadline = t.Kernel().MonotonicClock().Now().Add(timeoutDur)
   150  		haveDeadline = true
   151  	}
   152  
   153  	w, ch := waiter.NewChannelEntry(nil)
   154  	e.EventRegister(&w, waiter.ReadableEvents)
   155  	defer e.EventUnregister(&w)
   156  
   157  	// Try to read the events again until we succeed, timeout or get
   158  	// interrupted.
   159  	for {
   160  		r = e.ReadEvents(max)
   161  		if len(r) != 0 {
   162  			return r, nil
   163  		}
   164  
   165  		if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
   166  			if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
   167  				return nil, nil
   168  			}
   169  
   170  			return nil, err
   171  		}
   172  	}
   173  }