// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build zos && s390x package unix import ( "sync" ) // This file simulates epoll on z/OS using poll. // Analogous to epoll_event on Linux. // TODO(neeilan): Pad is because the Linux kernel expects a 96-bit struct. We never pass this to the kernel; remove? type EpollEvent struct { Events uint32 Fd int32 Pad int32 } const ( EPOLLERR = 0x8 EPOLLHUP = 0x10 EPOLLIN = 0x1 EPOLLMSG = 0x400 EPOLLOUT = 0x4 EPOLLPRI = 0x2 EPOLLRDBAND = 0x80 EPOLLRDNORM = 0x40 EPOLLWRBAND = 0x200 EPOLLWRNORM = 0x100 EPOLL_CTL_ADD = 0x1 EPOLL_CTL_DEL = 0x2 EPOLL_CTL_MOD = 0x3 // The following constants are part of the epoll API, but represent // currently unsupported functionality on z/OS. // EPOLL_CLOEXEC = 0x80000 // EPOLLET = 0x80000000 // EPOLLONESHOT = 0x40000000 // EPOLLRDHUP = 0x2000 // Typically used with edge-triggered notis // EPOLLEXCLUSIVE = 0x10000000 // Exclusive wake-up mode // EPOLLWAKEUP = 0x20000000 // Relies on Linux's BLOCK_SUSPEND capability ) // TODO(neeilan): We can eliminate these epToPoll / pToEpoll calls by using identical mask values for POLL/EPOLL // constants where possible The lower 16 bits of epoll events (uint32) can fit any system poll event (int16). // epToPollEvt converts epoll event field to poll equivalent. // In epoll, Events is a 32-bit field, while poll uses 16 bits. func epToPollEvt(events uint32) int16 { var ep2p = map[uint32]int16{ EPOLLIN: POLLIN, EPOLLOUT: POLLOUT, EPOLLHUP: POLLHUP, EPOLLPRI: POLLPRI, EPOLLERR: POLLERR, } var pollEvts int16 = 0 for epEvt, pEvt := range ep2p { if (events & epEvt) != 0 { pollEvts |= pEvt } } return pollEvts } // pToEpollEvt converts 16 bit poll event bitfields to 32-bit epoll event fields. func pToEpollEvt(revents int16) uint32 { var p2ep = map[int16]uint32{ POLLIN: EPOLLIN, POLLOUT: EPOLLOUT, POLLHUP: EPOLLHUP, POLLPRI: EPOLLPRI, POLLERR: EPOLLERR, } var epollEvts uint32 = 0 for pEvt, epEvt := range p2ep { if (revents & pEvt) != 0 { epollEvts |= epEvt } } return epollEvts } // Per-process epoll implementation. type epollImpl struct { mu sync.Mutex epfd2ep map[int]*eventPoll nextEpfd int } // eventPoll holds a set of file descriptors being watched by the process. A process can have multiple epoll instances. // On Linux, this is an in-kernel data structure accessed through a fd. type eventPoll struct { mu sync.Mutex fds map[int]*EpollEvent } // epoll impl for this process. var impl epollImpl = epollImpl{ epfd2ep: make(map[int]*eventPoll), nextEpfd: 0, } func (e *epollImpl) epollcreate(size int) (epfd int, err error) { e.mu.Lock() defer e.mu.Unlock() epfd = e.nextEpfd e.nextEpfd++ e.epfd2ep[epfd] = &eventPoll{ fds: make(map[int]*EpollEvent), } return epfd, nil } func (e *epollImpl) epollcreate1(flag int) (fd int, err error) { return e.epollcreate(4) } func (e *epollImpl) epollctl(epfd int, op int, fd int, event *EpollEvent) (err error) { e.mu.Lock() defer e.mu.Unlock() ep, ok := e.epfd2ep[epfd] if !ok { return EBADF } switch op { case EPOLL_CTL_ADD: // TODO(neeilan): When we make epfds and fds disjoint, detect epoll // loops here (instances watching each other) and return ELOOP. if _, ok := ep.fds[fd]; ok { return EEXIST } ep.fds[fd] = event case EPOLL_CTL_MOD: if _, ok := ep.fds[fd]; !ok { return ENOENT } ep.fds[fd] = event case EPOLL_CTL_DEL: if _, ok := ep.fds[fd]; !ok { return ENOENT } delete(ep.fds, fd) } return nil } // Must be called while holding ep.mu func (ep *eventPoll) getFds() []int { fds := make([]int, len(ep.fds)) for fd := range ep.fds { fds = append(fds, fd) } return fds } func (e *epollImpl) epollwait(epfd int, events []EpollEvent, msec int) (n int, err error) { e.mu.Lock() // in [rare] case of concurrent epollcreate + epollwait ep, ok := e.epfd2ep[epfd] if !ok { e.mu.Unlock() return 0, EBADF } pollfds := make([]PollFd, 4) for fd, epollevt := range ep.fds { pollfds = append(pollfds, PollFd{Fd: int32(fd), Events: epToPollEvt(epollevt.Events)}) } e.mu.Unlock() n, err = Poll(pollfds, msec) if err != nil { return n, err } i := 0 for _, pFd := range pollfds { if pFd.Revents != 0 { events[i] = EpollEvent{Fd: pFd.Fd, Events: pToEpollEvt(pFd.Revents)} i++ } if i == n { break } } return n, nil } func EpollCreate(size int) (fd int, err error) { return impl.epollcreate(size) } func EpollCreate1(flag int) (fd int, err error) { return impl.epollcreate1(flag) } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return impl.epollctl(epfd, op, fd, event) } // Because EpollWait mutates events, the caller is expected to coordinate // concurrent access if calling with the same epfd from multiple goroutines. func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return impl.epollwait(epfd, events, msec) }