mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Ensure that file closes, connection closes, etc are propagated
as interrupts in the interrupt controller.
Fixes: ff6a17989c ("Epoll based IRQ controller")
Signed-off-by: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
		
	
			
		
			
				
	
	
		
			147 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * Copyright (C) 2017 - Cambridge Greys Ltd
 | 
						|
 * Copyright (C) 2011 - 2014 Cisco Systems Inc
 | 
						|
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/epoll.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <string.h>
 | 
						|
#include <irq_user.h>
 | 
						|
#include <os.h>
 | 
						|
#include <um_malloc.h>
 | 
						|
 | 
						|
/* Epoll support */
 | 
						|
 | 
						|
static int epollfd = -1;
 | 
						|
 | 
						|
#define MAX_EPOLL_EVENTS 64
 | 
						|
 | 
						|
static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
 | 
						|
 | 
						|
/* Helper to return an Epoll data pointer from an epoll event structure.
 | 
						|
 * We need to keep this one on the userspace side to keep includes separate
 | 
						|
 */
 | 
						|
 | 
						|
void *os_epoll_get_data_pointer(int index)
 | 
						|
{
 | 
						|
	return epoll_events[index].data.ptr;
 | 
						|
}
 | 
						|
 | 
						|
/* Helper to compare events versus the events in the epoll structure.
 | 
						|
 * Same as above - needs to be on the userspace side
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
int os_epoll_triggered(int index, int events)
 | 
						|
{
 | 
						|
	return epoll_events[index].events & events;
 | 
						|
}
 | 
						|
/* Helper to set the event mask.
 | 
						|
 * The event mask is opaque to the kernel side, because it does not have
 | 
						|
 * access to the right includes/defines for EPOLL constants.
 | 
						|
 */
 | 
						|
 | 
						|
int os_event_mask(enum um_irq_type irq_type)
 | 
						|
{
 | 
						|
	if (irq_type == IRQ_READ)
 | 
						|
		return EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
 | 
						|
	if (irq_type == IRQ_WRITE)
 | 
						|
		return EPOLLOUT;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Initial Epoll Setup
 | 
						|
 */
 | 
						|
int os_setup_epoll(void)
 | 
						|
{
 | 
						|
	epollfd = epoll_create(MAX_EPOLL_EVENTS);
 | 
						|
	return epollfd;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper to run the actual epoll_wait
 | 
						|
 */
 | 
						|
int os_waiting_for_events_epoll(void)
 | 
						|
{
 | 
						|
	int n, err;
 | 
						|
 | 
						|
	n = epoll_wait(epollfd,
 | 
						|
		(struct epoll_event *) &epoll_events, MAX_EPOLL_EVENTS, 0);
 | 
						|
	if (n < 0) {
 | 
						|
		err = -errno;
 | 
						|
		if (errno != EINTR)
 | 
						|
			printk(
 | 
						|
				UM_KERN_ERR "os_waiting_for_events:"
 | 
						|
				" epoll returned %d, error = %s\n", n,
 | 
						|
				strerror(errno)
 | 
						|
			);
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
	return n;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper to add a fd to epoll
 | 
						|
 */
 | 
						|
int os_add_epoll_fd(int events, int fd, void *data)
 | 
						|
{
 | 
						|
	struct epoll_event event;
 | 
						|
	int result;
 | 
						|
 | 
						|
	event.data.ptr = data;
 | 
						|
	event.events = events | EPOLLET;
 | 
						|
	result = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
 | 
						|
	if ((result) && (errno == EEXIST))
 | 
						|
		result = os_mod_epoll_fd(events, fd, data);
 | 
						|
	if (result)
 | 
						|
		printk("epollctl add err fd %d, %s\n", fd, strerror(errno));
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper to mod the fd event mask and/or data backreference
 | 
						|
 */
 | 
						|
int os_mod_epoll_fd(int events, int fd, void *data)
 | 
						|
{
 | 
						|
	struct epoll_event event;
 | 
						|
	int result;
 | 
						|
 | 
						|
	event.data.ptr = data;
 | 
						|
	event.events = events;
 | 
						|
	result = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
 | 
						|
	if (result)
 | 
						|
		printk(UM_KERN_ERR
 | 
						|
			"epollctl mod err fd %d, %s\n", fd, strerror(errno));
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper to delete the epoll fd
 | 
						|
 */
 | 
						|
int os_del_epoll_fd(int fd)
 | 
						|
{
 | 
						|
	struct epoll_event event;
 | 
						|
	int result;
 | 
						|
	/* This is quiet as we use this as IO ON/OFF - so it is often
 | 
						|
	 * invoked on a non-existent fd
 | 
						|
	 */
 | 
						|
	result = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event);
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void os_set_ioignore(void)
 | 
						|
{
 | 
						|
	signal(SIGIO, SIG_IGN);
 | 
						|
}
 | 
						|
 | 
						|
void os_close_epoll_fd(void)
 | 
						|
{
 | 
						|
	/* Needed so we do not leak an fd when rebooting */
 | 
						|
	os_close_file(epollfd);
 | 
						|
}
 |