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 13:20:40 +0200
User-agent: Thunderbird 2.0.0.16 (Macintosh/20080707)

Thomas Bushnell BSG wrote:
On Sun, 2008-08-24 at 19:36 +0200, Da Zheng wrote:
As I said above, I have to make sure all ports created for the user program have been destroyed before the translator exits.

Are these ports that the translator has the receive rights to?  If so,
then when the translator exits, they will all be destroyed.
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.
When all user programs exit, the no-senders notification for the port is generated. If ports_port_deref() isn't called somewhere, the reference count of the port is still 2 when the no-senders notification comes, therefore, the port cannot be destroyed by ports_no_senders() or ports_do_mach_notify_no_senders() or something else.

Exactly.  That's because when you call ports_create_port, you create a
reference, and you must free it before that stack frame returns.

That's why I ask you for a suggestion where I should call ports_port_deref() to decrease the reference count when there is a sequence of ports_get_right() calls.

I'm keep telling you.  Calling ports_get_right does not mean you need to
call ports_port_deref, and never does in normal Hurd code.  But calling
ports_create_port does create a reference, and *that* reference must
indeed be freed, by ports_port_deref, before the calling routine is
done.
OK, It's clear for me now.
It's exactly what I did after I followed your suggestion to use the structure of proxy_user, proxy_device and proxy:-).
In the clean routine of proxy_user, I also freed the memory of proxy.
In the clean routine of proxy_device, I just set the pointer to the proxy_device in the proxy structure null (device->proxy->device = NULL).

But as you point out, the Mach device still has a reference to your
port, so you do need to destroy the right.  This is really because you
are using libports to serve a port which is not following normal Hurd
protocols.  (The port a Mach device sends filtered packets on is not
following Hurd protocols.)

Once you have things in a way you think looks good, why don't you post
the relevant code here, so that more eyes can look at it?
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;
};

struct proxy
{
 struct proxy_device *device;
 mach_port_t deliver_port;
 hurd_ihash_locp_t p_deliverport_hashloc;
 mach_port_t device_port;
};


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;
}

error_t
create_proxy_device (struct proxy *proxy, mach_port_t *port)
{
 error_t err;
 struct proxy_device *device;

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

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


Their cleaning routines are:

void
clean_proxy_user (void *p)
{
 struct proxy_user *user = p;
 struct proxy *proxy = user->proxy;

 if (proxy->p_deliverport_hashloc)
hurd_ihash_locp_remove (&proxy_deliverport_ht, proxy->p_deliverport_hashloc);

 if (proxy->deliver_port != MACH_PORT_NULL)
   mach_port_deallocate (mach_task_self (), proxy->deliver_port);
 if (proxy->device_port != MACH_PORT_NULL)
   mach_port_deallocate (mach_task_self (), proxy->device_port);

 if (proxy->device)
   ports_destroy_right (proxy->device);

 free (proxy);
}

void
clean_proxy_device (void *p)
{
 struct proxy_device *device = p;
 if (device->proxy)
   device->proxy->device = NULL;
}


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;
}

In ds_device_set_filter, I create the proxy_device object:
kern_return_t
ds_device_set_filter (device_t device, mach_port_t receive_port,
             int priority, filter_array_t filter, size_t filterlen)
{
 mach_port_t tmp;
 kern_return_t err;
 mach_port_t device_receive_port;
 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);

 if (proxy->device == NULL)
   {
     error_t err;
     err = create_proxy_device (proxy, &device_receive_port);
     if (err)
   return err;
   }
 else
   device_receive_port = ports_get_right (proxy->device);

 /* Set the filter from pfinet into the interface,
  * but the packet will be delivered to the translator,
  * so the translator has the chance to filter some packets. */
 err = device_set_filter (proxy->device_port,
              device_receive_port,
              MACH_MSG_TYPE_MAKE_SEND, priority,
              filter, filterlen);
 if (err)
   return err;

 proxy->deliver_port = receive_port;
 hurd_ihash_add (&proxy_deliverport_ht, receive_port, proxy);

 err = mach_port_request_notification (mach_task_self (), receive_port,
                   MACH_NOTIFY_DEAD_NAME, 0,
                   ports_get_right (notify_pi),
                   MACH_MSG_TYPE_MAKE_SEND_ONCE, &tmp);
 if (tmp != MACH_PORT_NULL)
   mach_port_deallocate (mach_task_self (), tmp);

 return err;
}


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.

Zheng Da




reply via email to

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