1

I wrote a chardevice that passes some messages received from the network to an user space application. The user space application has to both read the chardevice and send/receive messages via TCP sockets to other user-space applications. Both read and receiving should be blocking.

Since Libevent is able to handle multiple events at the same time, I thought registering an event for the file created by the chardevice and an event for a socket would just work, but I was wrong. But a chardevice creates a "character special file", and libevent seems to not be able to block. If I implement a blocking mechanism inside the chardevice, i.e. mutex or semaphore, then the socket event blocks too, and the application cannot receive messages.

The user space application has to accept outside connections at any time.

Do you know how to make it work? Maybe also using another library, I just want a blocking behaviour for both socket and file reader.

Thank you in advance.

Update: Thanks to @Ahmed Masud for the help. This is what I've done

Kernel module chardevice:
Implement a poll function that waits until new data is available

struct file_operations fops = {
  ...
  .read = kdev_read,
  .poll = kdev_poll,
};

I have a global variable to handle if the user space has to stop, and a wait queue:

static working = 1;
static wait_queue_head_t access_wait;

This is the read function, I return -1 if there is an error in copy_to_user, > 0 if everything went well, and 0 if the module has to stop. used_buff is atomic since it handles the size of a buffer shared read by user application and written by kernel module.

ssize_t
kdev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
  int error_count;

  if (signal_pending(current) || !working) { // user called sigint
    return 0;
  }

  atomic_dec(&used_buf);
  size_t llen = sizeof(struct user_msg) + msg_buf[first_buf]->size;
  error_count = copy_to_user(buffer, (char*)msg_buf[first_buf], llen);

  if (error_count != 0) {
    atomic_inc(&used_buf);
    paxerr("send fewer characters to the user");
    return error_count;
  } else
    first_buf = (first_buf + 1) % BUFFER_SIZE;

  return llen;
}

When there is data to read, I simply increment used_buf and call wake_up_interruptible(&access_wait). This is the poll function, I just wait until the used_buff is > 0

unsigned int
kdev_poll(struct file* file, poll_table* wait)
{
  poll_wait(file, &access_wait, wait);
  if (atomic_read(&used_buf) > 0)
    return POLLIN | POLLRDNORM;
  return 0;
}

Now, the problem here is that if I unload the module while the user space application is waiting, the latter will go into a blocked state and it won't be possible to stop it. That's why I wake up the application when the module is unloaded

void
kdevchar_exit(void)
{

  working = 0;
  atomic_inc(&used_buf); // increase buffer size to application is unlocked
  wake_up_interruptible(&access_wait); // wake up application, but this time read will return 0 since working = 0;
  ... // unregister everything
}

User space application
Libevent by default uses polling, so simply create an event_base and a reader event.

base = event_base_new();
filep = open(fname, O_RDWR | O_NONBLOCK, 0);
evread = event_new(base, filep, EV_READ | EV_PERSIST,
                                on_read_file, base);

where on_read_file simply reads the file, no poll call is made (libevent handles that):

static void
on_read_file(evutil_socket_t fd, short event, void* arg)
{
  struct event_base* base = arg;

  int len = read(...);
  if (len < 0) 
    return;

  if (len == 0) {
    printf("Stopped by kernel module\n");
    event_base_loopbreak(base);
    return;
  }
  ... // handle message
}
John
  • 121
  • 3
  • 14
  • Time to re-read [the help pages](http://stackoverflow.com/help), re-take [the SO tour](http://stackoverflow.com/tour), re-read [how to ask good questions](http://stackoverflow.com/help/how-to-ask), and re-learn how to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve). I also recommend [this SO question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/) and [learning how to debug your programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – Some programmer dude May 05 '18 at 09:19
  • 1
    There are several approaches, you could stick to libevent but you'd have to add nonblocking or polling I/O to your char device. have a look at https://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code – Ahmed Masud May 05 '18 at 09:23
  • @Ahmed Masud thank you, it seems to work fine! just another question, how can I interrupt the polling? Right now if I firstly detach the chardevice while the user space application is still listening, the application will block and it won't be possible to stop it anymore, because the polling is done internally by libevent. Any ideas? If you want, I can post the code – John May 05 '18 at 14:14
  • 1
    never mind, solved – John May 05 '18 at 16:11
  • please post the code any how because it may help someone else :-) – Ahmed Masud May 05 '18 at 21:09
  • better yet answer your own question :-) – Ahmed Masud May 05 '18 at 21:09

0 Answers0