bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Some questions about libports and notification of ports


From: Da Zheng
Subject: Re: Some questions about libports and notification of ports
Date: Mon, 25 Aug 2008 19:31:01 +0200
User-agent: Thunderbird 2.0.0.16 (Macintosh/20080707)

Thomas Bushnell BSG wrote:
On Mon, 2008-08-25 at 13:20 +0200, Da Zheng wrote:
Yes, these ports have the receive rights.
They are used for the user program, and I want to make sure they have all been destroyed before the translator exits, so the translator knows all user programs connected to it have all exited and it can exit without causing any problems. I think every translator does it. For example, pfinet checks "ports_count_class (socketport_class) != 0" in its trivfs_goaway(). If the count of socket port class isn't 0, pfinet doesn't exit.

If you are exiting in the goaway routine, this is the right thing,
because the goaway routine is really about detaching from the parent
filesystem, and then exiting only once your users are done.

pfinet is not destroying any ports here, which is quite different from
what you asked.  If you want to make sure there are no ports, the thing
pfinet does is right.  If you want to destroy ports unconditionally,
then exit, then you can just exit.  That will automatically deallocate
all the ports in the task, including its receive rights.
I don't want to destroy the port. I just want to check if all ports have already been destroyed before the goaway routine is called.
Right, I define three structures. My translator still run in one thread, so I don't define a lock. and it's very uncommon for the device to exit before the user program exits, so I don't destroy the port for the user when the port for the device is destroyed. Actually the user program will find it anyway.

struct proxy_user
{
  struct port_info pi;
  struct proxy *proxy;
};

struct proxy_device
{
  struct port_info pi;
  struct proxy *proxy;
};

Note that if you don't have any difference between these structures--if
there is nothing extra you need--you can combine them into one name.
Port classes will keep them separate for practical purposes.
I want to separate the ports for the user and for the device clearly, and it's easier for me to name the structures.
To create the proxy_user and proxy_device objects:

error_t
create_proxy_user (struct proxy *proxy, mach_port_t *port)
{
  error_t err;
  struct proxy_user *user;

err = ports_create_port (user_portclass, port_bucket, sizeof (*user), &user);
  if (err)
      return err;
  user->proxy = proxy;

  *port = ports_get_right (user);
  ports_port_deref (user);
  return 0;
}

Just to repeat here, b/c there was confusion before.  The
ports_port_deref you call here has nothing to do with the
ports_get_right call.  Nothing at all.  It is there because of the
ports_create_port call.
I'm clear now. Thank you.
Here are the most important server side functions in device.defs.
In ds_device_open(), I create the proxy_user and proxy object.

kern_return_t
ds_device_open (mach_port_t master_port, mach_port_t reply_port,
        mach_msg_type_name_t reply_portPoly,
        dev_mode_t mode, dev_name_t name, mach_port_t *device,
        mach_msg_type_name_t *devicetype)
{
  kern_return_t err;
  mach_port_t master_device;
  mach_port_t user_port;
  struct proxy *proxy;

  if (device_file == NULL)
    return D_NO_SUCH_DEVICE;

  master_device = file_name_lookup (device_file, 0, 0);
  if (master_device == MACH_PORT_NULL)
    return errno;

  proxy = (struct proxy *)calloc (1, sizeof (*proxy));
  if (proxy == NULL)
    {
      mach_port_deallocate (mach_task_self (), master_device);
      return D_NO_MEMORY;
    }

  err = device_open (master_device, mode, name, &proxy->device_port);
  mach_port_deallocate (mach_task_self (), master_device);
  if (err != KERN_SUCCESS)
    {
      free (proxy);
      return err;
    }

  err = create_proxy_user (proxy, &user_port);
  if (err)
    {
      mach_port_deallocate (mach_task_self (), master_device);
      free (proxy);
      return err;
    }

  *device = user_port;
  *devicetype = MACH_MSG_TYPE_MAKE_SEND;

  return 0;
}

What is device_file set to?
The device file is created by a translator to help other user programs to open the device. For example, we can create a device file /dev/eth0 with the translator. When the user program calls file_name_lookup() on the file and device_open() as I do in the code, I actually open the device of eth0.
So device_file here can be /dev/eth0, /dev/eth1, etc.
Note that network device users are not obliged to use device_set_filter!
They can just use device_read and device_write as normal if they please.
You'll need to implement the whole device interface.
I implemented device_write(). When the request comes, I apply some filtering rules to the packet.

kern_return_t
ds_device_write (device_t device, mach_port_t reply_port,
        mach_msg_type_name_t reply_type, dev_mode_t mode,
        recnum_t recnum, io_buf_ptr_t data, size_t datalen,
        int *bytes_written)
{
 int ret_count = 0;
 int has_filter = 0;
 net_hash_entry_t entp, *hash_headp;
 net_rcv_port_t infp, nextfp;
 struct proxy_user *user;
 struct proxy *proxy;

 user = ports_lookup_port (port_bucket, device, user_portclass);
 if (user == NULL)
   return D_INVALID_OPERATION;
 proxy = user->proxy;
 ports_port_deref (user);

 /* The packet can be sent as long as it passes one filter,
  * even thought there is usually only one filter in the list. */
 FILTER_ITERATE (&snd_port_list, infp, nextfp, &infp->chain)
   {
     has_filter = 1;
     ret_count = mach_bpf_do_filter (infp,
                     data + sizeof (struct ethhdr),
                     datalen - sizeof (struct ethhdr),
                     data, sizeof (struct ethhdr),
                     &hash_headp, &entp);
     if (ret_count)
   break;
   }
 FILTER_ITERATE_END

   if (ret_count || !has_filter)
     {
   error_t err;
   print_pack (data, datalen);
   err = device_write (proxy->device_port, mode , recnum ,
               data, datalen, bytes_written);
   return err;
     }
   else
     {
   *bytes_written = datalen;
   return 0;
     }
}


But I don't know device_read() can also be used by the network device, so I just simply forward the request. I need to fix it.

kern_return_t
ds_device_read (device_t device, mach_port_t reply_port,
       mach_msg_type_name_t reply_type, dev_mode_t mode,
       recnum_t recnum, int bytes_wanted,
       io_buf_ptr_t *data, size_t *datalen)
{
 kern_return_t ret;
 struct proxy_user *user;
 struct proxy *proxy;

 user = ports_lookup_port (port_bucket, device, user_portclass);
 if (user == NULL)
   return D_INVALID_OPERATION;
 proxy = user->proxy;
 ports_port_deref (user);

 ret = device_read (proxy->device_port, mode, recnum,
            bytes_wanted, data, datalen);
 return ret;
}
Since eth-filter works as a proxy for several user programs, it can receive many device_open requests. Should I call device_open only once in eth-filter or call it as long as a device_open request comes? eth-filter connects to only one device, so device_open always returns the same send right, even though the mode of opening device is changed.
I don't know if it will cause a problem.

I would just open it once, with maximal permissions, and then manage the
permissions yourself.
I see. Thank you.

Zheng Da




reply via email to

[Prev in Thread] Current Thread [Next in Thread]