[openbeosnetteam] Re: Progress report

  • From: philippe.houdoin@xxxxxxx
  • To: openbeosnetteam@xxxxxxxxxxxxx
  • Date: Wed, 13 Mar 2002 17:33:37 +0100 (MET)

Okay, I'm starting to feel mad to have both *boring* job days AND no CVS access 
to OpenBeOS code repository, thanks to my company firewall policy :-(

So, let's try to write the longest post on this ML ever...
:-)

> We can now build ourselves as a kernel driver. [...]
> This is the start of allowing ourselves to be very cool and do 
> all sorts of stuff, as well as simplifying our i/o with regard to 
> running as a process. developing our own IPC so we could be 
> running the net stack in one team while other teams used the 
> services was looking like a PITA and would have been a performance 
> drag in the extreme. So, the solution?
>
> Basically we'll build the stack in a series of pieces that live 
> in the kernel.

I can't agree more on this shift of stack design :-)

> - socket driver - this will be built mainly from code that is already 
> in place in net-server/net_server.c, albeit it'll be removed from 
> there and added to a separate file that will build a simple socket 
> driver. 
> This is responsible for creating a /dev/net/socket entry and 
> handling requests to/from that socket. 

Hum, nobody want to use my submitted (and commited to CVS) driver
net_kit/source/kernel/net_stack_driver.c?! 
It does exactly what we need: a device driver wrapper between clients (via 
libnet.so) and our future kernel-living network stack.

Just modify the NET_STACK_DRIVER_PATH constant in 
net_kit/source/server/include/stack_driver.h to let him publish 
the /dev/net/socket entry instead of /dev/net/stack...

What's only missing there is glue code to pass the call he receive 
to the *core* network stack module. 
More in this "core network kernel module" point below...

> When started for the first time it will load the net-server kernel 
> module.

What about calling it the network stack kernel module(s) now, as the 
concept of "servers" may being miss-interpreted by [Open]BeOS community 
members with the well-known userland servers concept?

> - kernel modules - these will be everything else. 
> All the existing modules need to be rewritten to allow them to 
> be kernel modules and then we need to decide where they should be 
> installed. 

I suggest we put them here:
/boot/home/config/add-ons/kernel/network/*

get_module() and open_module_list() always look for a module_name in 
this order:
/boot/home/config/add-ons/kernel/*
/boot/beos/system/add-ons/kernel/*

For example, get_module("network/core/v1") will look for a file named

/boot/home/config/add-ons/kernel/network
/boot/home/config/add-ons/kernel/network/core
/boot/home/config/add-ons/kernel/network/core/v1
/boot/beos/system/add-ons/kernel/network
/boot/beos/system/add-ons/kernel/network/core
/boot/beos/system/add-ons/kernel/network/core/v1

with a "modules" data symbol publishing a "network/core/v1" module_info name.

On a side note, this would prevent collision with existing
/boot/beos/system/add-ons/kernel/network/* in BONE-based BeOS systems, too:
as all BONE modules are named bone_xxxx, we should not encounter 
module name collision problem until we don't prefix our modules "bone_*".

We can also create sub-directories for specific network sub-modules type 
(interface, protocol, framing, whatever).
BTW, I doubt we could have the same module interface (public functions) 
for all of our modules types (protocols, interfaces, core module).

> - What functions do we need in the core module? Basically what do we want 
> to export and how does the socket driver talk to the core module? 
> We need ioctl for sure as that's how most of the functions work, and 
> we'll need an init/uninit (or do we want to call it start/stop) but 
> can anyone think of other things we need?

Here come my so-called "core network kernel module" point:

The /dev/net/socket device driver don't need to do more than simply load 
the network/core kernel module at init_driver() time, 
pass each call he receive on any socket to corresponding functions 
exported by the core module and unload the core module at uninit_driver() 
time (which would do nothing, as the core module will ask to be keep loaded 
via the B_KEEP_LOADED module_info flag. 

The *link* between the driver 
and the core module would be a opaque thing, which I've called a net_endpoint 
pointer in my current net_kit/source/kernel/net_stack_driver.c implementation:

---- network_core_module.h ----
// Definition of the public interface 
// exported by the network core kernel module

#define NETWORK_CORE_MODULE_NAME  "network/core"

typedef struct network_core_module_info
{
  struct module_info info;
  
  // /dev/net/socket driver <-> network core module glue functions

  status_t (*endpoint)(net_endpoint ** ne, int family, int type, 
                       int protocol);
  status_t (*open)(net_endpoint * ne);
  status_t (*close)(net_endpoint * ne);
  status_t (*free)(net_endpoint * ne);
  
  status_t (*read)(net_endpoint * ne, off_t pos, void *data, size_t *len);
  status_t (*write)(net_endpoint * ne, off_t pos, void *data, size_t *len);
  status_t (*readv)(net_endpoint * ne, off_t pos, iovec *vec, size_t nb, 
                    size_t *len);
  status_t (*writev)(net_endpoint * ne, off_t pos, iovec *vec, size_t nb,
                     size_t *len);
  status_t (*control)(net_endpoint * ne, uint32 code, void *data, size_t len);
  
  // BSD sockets api
  int      (*connect)(net_endpoint * ne, struct sockaddr * addr, int addr_len);
  int      (*shutdown)(net_endpoint * ne, int direction);
  int      (*bind)(net_endpoint * ne, struct sockaddr * addr, int addr_len);
  int      (*listen)(net_endpoint * ne, int backlog);
  int      (*accept)(net_endpoint * ne, net_endpoint ** ne, 
                     struct sockaddr * addr, int * addr_len);
  int      (*send)(net_endpoint * ne, void * data, size_t len, int flags);
  int      (*sendto)(net_endpoint * ne, void * data, size_t len, 
                     int flags, struct sockaddr * addr, int addr_len);
  int      (*recv)(net_endpoint * ne, void * data, size_t len, int flags);
  int      (*recvfrom)(net_endpoint * ne, void * data, size_t datalen, 
                       int flags, struct sockaddr * addr, int *addr_len);
  int      (*getpeername)(net_endpoint * ne, 
                          struct sockaddr * addr, int * addr_len);
  int      (*getsockname)(net_endpoint * ne, 
                          struct sockaddr *addr, int * addr_len);
  int      (*getsockopt)(net_endpoint * ne, int level, int option, 
                        void * optval, int * optlen);
  int      (*setsockopt)(net_endpoint * ne, int level, int option, 
                        void * optval, int optlen);
  
} network_core_module_info;

---- network_core_module.c ----

// TODO: :-p
// glue each core module public functions to current net_srv implementation

status_t endpoint(net_endpoint ** ne, int family, int type, int protocol)
{
  struct sock_fd * sfd;

  if (*ne == NULL) {
    sfd = make_sock_fd();
    if (! sfd)
       return B_NO_MEMORY;
    *ne = sfd;
    return B_OK;
  };
  
  sfd = (struct sock_fd *) *ne;
  // now set to this previously created sock_fd family, type and protocol...
  ...
  return B_OK;
}


status_t free(net_endpoint * ne)
{
  release_sock_fd((struct sock_fd *) ne);
  return B_OK;
}

...

---- net_kit/source/kernel/net_stack_driver.c ----

static network_core_module_info * g_net_core = NULL;


_EXPORT status_t init_driver(void)
{
  status_t status;
      
  status = get_module(NETWORK_CORE_MODULE_NAME, 
                      (module_info **) &g_net_core);
  if ( status != B_OK ) {
    dprintf(LOGID ERR "Argh! Can't load network core module: %s\n", 
            strerror(status)); 
    return status;
  };

  return B_OK;
}


_EXPORT void uninit_driver(void)
{
  put_module(NETWORK_CORE_MODULE_NAME);
}


static status_t device_open(const char * name, uint32 flags, void ** cookie)
{
  net_endpoint *  ep;
  status_t        status;

  ep = NULL; // ask network core module for a new, untyped endpoint
  status = g_net_core->endpoint(&ep, 0, 0, 0);
  if (status != B_OK || ep == NULL)
    return status;

  // attach this net_endpoint to file descriptor cookie
  *cookie = ep;
  return B_OK;
}


static status_t device_close(void * cookie)
{
  // notify the network core module that this endpoint is closing...
  return g_net_core->close((net_endpoint *) cookie);
}


static status_t device_free(void * cookie)
{
  return g_net_core->free((net_endpoint *) cookie);
}


static status_t device_control(void * cookie, uint32 op, void * data, 
                               size_t len)
{
  net_endpoint * ep = (net_endpoint *) cookie;

  switch (op) {
  case NET_STACK_SOCKET:
    {
    socket_ioctl * ctl = (socket_ioctl *) data;
    // thos time, ask network core module to init our endpoint
    return g_net_core->endpoint(&ep, ctl->family, ctl->type, ctl->proto);
    };

  case NET_STACK_SHUTDOWN:
    {
    int_ioctl * ctl = (int_ioctl *) data;
    return g_net_core->shutdown(ep, ctl->value);
    };

  case NET_STACK_CONNECT:
    {
    sockaddr_ioctl * ctl = (sockaddr_ioctl *) data;
    return g_net_core->connect(ep, ctl->addr, ctl->len);
    };

  case ...

  default:
    return g_net_core->control(ep, op, data, len);
  };

  return B_UNSUPPORTED;
}


static status_t device_read(void * cookie, off_t pos, void * data, 
                            size_t * len)
{
  return g_net_core->read((net_endpoint *) cookie, pos, data, len);
}

[...]

Same idea for all device driver hooks: pass them 
to the corresponding network core module calls...

This way, we isolate the device driver code from the network core module one, 
the net_endpoint * doing the media between the two.
For the driver, it's an opaque thing, for the network core module, it could be 
a struct socket *.

In this pseudo-code, I use the struct sock_fd * because I can directly use 
the struct socket *, as for one to be valid socreate() require that the 
family/type/protocol are valid, which I haven't in my endpoint() code.
Or did I miss a create_uninited_socket_struct() somewhere?

But using the sock_fd structure add a useless layer, as the devfs already 
handle the file descriptor <-> socket pairing for us, with the help of 
/dev/net/socket driver "cookies"...

With this mechanism, no need to work on the driver code ever. ;-)
  
> Todo
> 
> Well, the above really! First things to be done I think are as follows...
> 1. create the new socket driver and get it working.
> 2. get the core code we have working as a kernel module
> 3. start getting the other modules working as kernel modules and 
> figure out where to install them

Absolutly.

Points 1 & 2: I can write a network core module skeleton tonigh if you 
like, but as I'm not yet fluent with the current BSD-based 
net_kit/source/server/* code, I'm not sure I can, well, glue all together to 
make the stack really works... not in that short time. :-(

For point 3, I guess the ethernet module is first candidate.
We'll have to define the network kernel sub-modules interface. 
As I've previously write, I doubt we can have a generic interface 
for all kind of network sub-module. 
For example, interface sub-modules would have some up(), down(), input() and 
output() but no resolve()/lookup(). For protocol sub-modules, up() and down() 
don't mean anything... well

> get ready for some nasty debugging experience and dig out the old 
> serial dbeugging cable as this is a truly useful tool to use.

Hey, there is a another way:
http://www.betips.net/chunga.php?ID=540

Hint: sometimes, tail -f block forever, you don't see any new message, 
when in fact there is. Just stop it and re-run.

-Philippe

Other related posts: