Thanks a lot Ben for your review. I will apply your advice in v2. Yifeng
On Wed, Dec 13, 2017 at 11:02 AM, Ben Pfaff <b...@ovn.org> wrote: > On Mon, Dec 04, 2017 at 06:03:40AM -0800, Yifeng Sun wrote: > > This patch is a simple implementation for the proposal discussed in > > https://mail.openvswitch.org/pipermail/ovs-dev/2017-August/337038.html > and > > https://mail.openvswitch.org/pipermail/ovs-dev/2017-October/340013.html. > > > > It enables ovs-vswitchd to use DNS names when specifying OpenFlow and > > OVSDB remotes. > > > > Below are some of the features and limitations of this patch: > > The resolving is asynchornous, so it doesn't block the main loop; > > Both IPv4 and IPv6 are supported; > > The resolving API is thread-safe; > > Depends on the unbound library; > > When multiple ip addresses are returned, only the first one is > used; > > /etc/nsswitch.conf isn't respected as unbound library doesn't look > at it; > > Depends on caller to retry later to use the resolved results. > > > > Signed-off-by: Yifeng Sun <pkusunyif...@gmail.com> > > Thanks a lot! DNS support will be a great feature for our users. > > I have some comments. > > Usually, OVS functions use one of two calling conventions for reporting > errors. coding-style.rst describes them this way: > > For functions that return a success or failure indication, prefer > one of the following return value conventions: > > - An ``int`` where 0 indicates success and a positive errno value > indicates a reason for failure. > > - A ``bool`` where true indicates success and false indicates > failure. > > The convention used here for dns_resolve() is different. I'd prefer to > see it adopt one of the above styles. > > Usually, we use a .h file for high-level documentation of a particular > code module, and then document particular functions in the .c file. > Since this module has very few public functions, this convention is not > as important, but I would still like to see it followed. > > Usually, we use OVS_LIKELY and OVS_UNLIKELY only in > performance-sensitive inner loops. I don't think that any of the DNS > code qualifies. > > I am concerned about what could happen if the unbound library cannot be > initialized. In that case, I think that every call to dns_resolve() > will try to initialize it again. That is likely to log error messages > at a high rate. I suggest either rate-limiting the log messages (with > VLOG_ERR_RL, etc.) or changing the initialization code so that it only > tries to initialize once and then permanently fails. My guess is that > the latter is a better choice in this case. > > Probably, ub_process() error logging should also be rate-limited. It is > also worth thinking about resolve_callback__()'s logging, to figure out > whether it is likely to be too voluminous in practice. > > I think that resolve_find_or_new__() could be made very slightly simpler > by using shash_find_data(). > > xcalloc(1, x) can be written xzalloc(x). > > In some of the dns_resolve() error cases, *addr is not set to NULL. > It's a better user interface if *addr is always set to NULL on error. > > These days, we tend to try to declare and initialize variables at the > same point, so in dns_resolve() I would move the declarations of 'req' > and 'retval' down to their points of first use. > > coding-style.rst says: > > Do parenthesize a subexpression that must be split across more than > one line, e.g.:: > > *idxp = ((l1_idx << PORT_ARRAY_L1_SHIFT) | > (l2_idx << PORT_ARRAY_L2_SHIFT) | > (l3_idx << PORT_ARRAY_L3_SHIFT)); > so that: > return req != NULL > && req->state == RESOLVE_GOOD > && !resolve_check_expire__(req); > should become: > return (req != NULL > && req->state == RESOLVE_GOOD > && !resolve_check_expire__(req)); > > resolve_set_time__() seems simple enough to me that the code would be > clearer if it were just written inline instead of as a function. > > I am not sure why the 'addr' member of struct resolve_request is marked > const. I do not understand the benefit in this case. > > I see better why the 'name' member of struct resolve_request is marked > const. It is because it duplicates the name in the shash_node and > should not be separately freed. But I think that it would be a cleaner > design in this case to use an hmap directly, by adding an hmap_node > member to struct resolve_request and then changing all_reqs__ from > struct shash to struct hmap. > > I do not understand why all_reqs exists. It seems that, each time it is > used, one could write &all_reqs__ instead. > > Initialization in dns_resolve() and destruction in dns_resolve_destroy() > seems asymmetric in at least one way: dns_resolve_destroy() destroys > all_reqs, but dns_resolve_init__() does not initialize all_reqs. I > believe that this means that calling dns_resolve_destroy(), then > resolving an address, will cause a segfault. It is better to avoid that > potential problem. > > In resolve_callback__(), the cast here is not needed: > struct resolve_request *req; > > req = (struct resolve_request *)data; > I would write it as: > struct resolve_request *req = data; > > Similarly, in resolve_async__(), this cast is not needed either: > qtype, ns_c_in, (void *)req, > > Actually, common OVS style in a case like this is to give the parameter > a _ suffix, like this: > > static void > resolve_callback__(void *req_, int err, struct ub_result *result) > OVS_REQUIRES(dns_mutex__) > { > struct resolve_request *req = req_; > > Thanks, > > Ben. > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev