github.com/jspc/eggos@v0.5.1-0.20221028160421-556c75c878a5/kernel/epoll.go (about)

     1  package kernel
     2  
     3  import (
     4  	"syscall"
     5  	"unsafe"
     6  
     7  	"github.com/jspc/eggos/kernel/isyscall"
     8  	"github.com/jspc/eggos/kernel/mm"
     9  	"gvisor.dev/gvisor/pkg/abi/linux"
    10  	"gvisor.dev/gvisor/pkg/abi/linux/errno"
    11  )
    12  
    13  const (
    14  	epollFd = 3
    15  
    16  	maxFds = 1024
    17  )
    18  
    19  var (
    20  	// source of fd events, set by netstack
    21  	// cleared by epoll_wait
    22  	fdevents [maxFds]uint32
    23  
    24  	// to manage epoll event
    25  	eventpool mm.Pool
    26  
    27  	// header of registered epoll events
    28  	epollEvents epollEvent
    29  
    30  	// notify of epoll events
    31  	epollNote note
    32  )
    33  
    34  //go:notinheap
    35  type epollEvent struct {
    36  	fd  uintptr
    37  	sub linux.EpollEvent
    38  
    39  	pre, next *epollEvent
    40  }
    41  
    42  //go:nosplit
    43  func newEpollEvent() *epollEvent {
    44  	ptr := eventpool.Alloc()
    45  	e := (*epollEvent)(unsafe.Pointer(ptr))
    46  	e.pre = &epollEvents
    47  	e.next = epollEvents.next
    48  	if epollEvents.next != nil {
    49  		epollEvents.next.pre = e
    50  	}
    51  	epollEvents.next = e
    52  	return e
    53  }
    54  
    55  //go:nosplit
    56  func freeEpollEvent(e *epollEvent) {
    57  	e.pre.next = e.next
    58  	if e.next != nil {
    59  		e.next.pre = e.pre
    60  	}
    61  	eventpool.Free(uintptr(unsafe.Pointer(e)))
    62  }
    63  
    64  //go:nosplit
    65  func findEpollEvent(fd uintptr) *epollEvent {
    66  	for e := epollEvents.next; e != nil; e = e.next {
    67  		if e.fd == fd {
    68  			return e
    69  		}
    70  	}
    71  	return nil
    72  }
    73  
    74  //go:nosplit
    75  func epollCtl(epfd, op, fd, desc uintptr) uintptr {
    76  	euser := (*linux.EpollEvent)(unsafe.Pointer(desc))
    77  	var e *epollEvent
    78  	switch op {
    79  	case syscall.EPOLL_CTL_ADD:
    80  		e = findEpollEvent(fd)
    81  		if e == nil {
    82  			e = newEpollEvent()
    83  		}
    84  		e.fd = fd
    85  		e.sub = *euser
    86  		e.sub.Events |= syscall.EPOLLHUP
    87  		return 0
    88  	case syscall.EPOLL_CTL_MOD:
    89  		e = findEpollEvent(fd)
    90  		if e == nil {
    91  			return isyscall.Errno(errno.EINVAL)
    92  		}
    93  		e.sub = *euser
    94  		e.sub.Events |= syscall.EPOLLHUP
    95  		return 0
    96  	case syscall.EPOLL_CTL_DEL:
    97  		e = findEpollEvent(fd)
    98  		if e == nil {
    99  			return isyscall.Errno(errno.EINVAL)
   100  		}
   101  		freeEpollEvent(e)
   102  		return 0
   103  	default:
   104  		return isyscall.Errno(errno.EINVAL)
   105  	}
   106  }
   107  
   108  //go:nosplit
   109  func epollWait(epfd, eventptr, len, _ms uintptr) uintptr {
   110  	if _ms != 0 {
   111  		ts := linux.Timespec{
   112  			Sec:  int64(_ms / 1000),
   113  			Nsec: int64(_ms%1000) * ms,
   114  		}
   115  		// wait fd event
   116  		epollNote.sleep(&ts)
   117  		epollNote.clear()
   118  	}
   119  
   120  	events := (*[256]linux.EpollEvent)(unsafe.Pointer(eventptr))[:len]
   121  	var cnt uintptr = 0
   122  	for e := epollEvents.next; e != nil && cnt < len; e = e.next {
   123  		event := fdevents[e.fd]
   124  		if event == 0 {
   125  			continue
   126  		}
   127  		ue := &events[cnt]
   128  		ue.Data = e.sub.Data
   129  		ue.Events = event & e.sub.Events
   130  		// clear events
   131  		// FIXME: only clear masked events?
   132  		fdevents[e.fd] = 0
   133  		cnt++
   134  	}
   135  	return cnt
   136  }
   137  
   138  //go:nosplit
   139  func epollNotify(fd, events uintptr) {
   140  	fdevents[fd] |= uint32(events)
   141  	epollNote.wakeup()
   142  }
   143  
   144  //go:nosplit
   145  func epollInit() {
   146  	mm.PoolInit(&eventpool, unsafe.Sizeof(epollEvent{}))
   147  }