platform: use NMDedupMultiIndex for routes in NMPCache

Rework platform object cache to use NMDedupMultiIndex.

Already previously, NMPCache used NMMultiIndex and had thus
O(1) for most operations. What is new is:

- Contrary to NMMultiIndex, NMDedupMultiIndex preserves the order of
  the cached items. That is crucial to handle routes properly as kernel
  will replace the first matching route based on network/plen/metric
  properties. See related bug rh#1337855.
  Without tracking the order of routes as they are exposed
  by kernel, we cannot properly maintain the route cache.

- All NMPObject instances are now treated immutable, refcounted
  and get de-duplicated via NMDedupMultiIndex. This allows
  to have a global NMDedupMultiIndex that can be shared with
  NMIP4Config and NMRouteManager. It also allows to share the
  objects themselves.
  Immutable objects are so much nicer. We can get rid of the
  update pre-hook callback, which was required previously because
  we would mutate the object inplace. Now, we can just update
  the cache, and compare obj_old and obj_new after the fact.

- NMMultiIndex was treated as an internal of NMPCache. On the other
  hand, NMDedupMultiIndex exposes NMDedupMultiHeadEntry, which is
  basically an object that allows to iterate over all related
  objects. That means, we can now lookup objects in the cache
  and give the NMDedupMultiHeadEntry instance to the caller,
  which then can iterate the list on it's own -- without need
  for copying anything.
  Currently, at various places we still create copies of lookup
  results. That can be improved later.

The ability to share NMPObject instances should enable us to
significantly improve performance and scale with large number
of routes.

Of course there is a memory overhead of having an index for each list
entry. Each NMPObject may also require an NMDedupMultiEntry,
NMDedupMultiHeadEntry, and NMDedupMultiBox item, which are tracked
in a GHashTable. Optimally, one NMDedupMultiHeadEntry is the head
for multiple objects, and NMDedupMultiBox is able to deduplicate several
NMPObjects, so that there is a net saving.
Also, each object type has several indexes of type NMPCacheIdType.
So, worst case an NMPlatformIP4Route in the platform cache is tracked
by 8 NMPCacheIdType indexes, for each we require a NMDedupMultiEntry,
plus the shared NMDedupMultiHeadEntry. The NMDedupMultiBox instance
is shared between the 8 indexes (and possibly other).
This commit is contained in:
Thomas Haller 2017-06-21 10:53:34 +02:00
parent 3edf2bff3d
commit 9440eefb6d
8 changed files with 1691 additions and 1297 deletions

View file

@ -1173,7 +1173,7 @@ nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *sel
? "update" : "new",
nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
g_hash_table_replace (priv->ip4_device_routes.entries,
nmp_object_ref (entry->obj),
(NMPObject *) nmp_object_ref (entry->obj),
entry);
}
if (priv->ip4_device_routes.gc_id == 0) {

View file

@ -255,8 +255,11 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType act
static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink);
static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name);
static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type);
static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType cache_op, gpointer user_data);
static void cache_prune_candidates_prune (NMPlatform *platform);
static void cache_on_change (NMPlatform *platform,
NMPCacheOpsType cache_op,
const NMPObject *obj_old,
const NMPObject *obj_new);
static void cache_prune_all (NMPlatform *platform);
static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks);
static void ASSERT_NETNS_CURRENT (NMPlatform *platform);
@ -1680,7 +1683,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
* Also, sometimes the info-data is missing for updates. In this case
* we want to keep the previously received lnk_data. */
nmp_object_unref (lnk_data);
lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk);
lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk);
}
if (address_complete_from_cache)
obj->link.addr = link_cached->link.addr;
@ -2574,7 +2577,9 @@ typedef struct {
GIOChannel *event_channel;
guint event_id;
gboolean sysctl_get_warned;
bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
bool sysctl_get_warned;
GHashTable *sysctl_get_prev_values;
NMUdevClient *udev_client;
@ -2595,8 +2600,6 @@ typedef struct {
gint is_handling;
} delayed_action;
GHashTable *prune_candidates;
GHashTable *wifi_data;
} NMLinuxPlatformPrivate;
@ -2913,29 +2916,20 @@ process_events (NMPlatform *platform)
/*****************************************************************************/
#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \
({ \
NMPCacheId _cache_id; \
\
((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \
nmp_cache_id_init_object_type (&_cache_id, (obj_type), (visible_only)), \
NULL)); \
})
/*****************************************************************************/
static void
do_emit_signal (NMPlatform *platform, const NMPObject *obj_new, NMPCacheOpsType cache_op, gboolean was_visible)
do_emit_signal (NMPlatform *platform,
NMPCacheOpsType cache_op,
const NMPObject *obj_old,
const NMPObject *obj_new)
{
gboolean is_visible;
NMPObject obj_clone;
gboolean visible_new;
gboolean visible_old;
const NMPObject *o;
const NMPClass *klass;
nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED));
nm_assert (obj_new || cache_op == NMP_CACHE_OPS_UNCHANGED);
nm_assert (!obj_new || cache_op == NMP_CACHE_OPS_REMOVED || obj_new == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_new));
nm_assert (!obj_new || cache_op != NMP_CACHE_OPS_REMOVED || obj_new != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_new));
ASSERT_nmp_cache_ops (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, cache_op, obj_old, obj_new);
ASSERT_NETNS_CURRENT (platform);
@ -2943,52 +2937,49 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj_new, NMPCacheOpsType
case NMP_CACHE_OPS_ADDED:
if (!nmp_object_is_visible (obj_new))
return;
o = obj_new;
break;
case NMP_CACHE_OPS_UPDATED:
is_visible = nmp_object_is_visible (obj_new);
if (!was_visible && is_visible)
visible_old = nmp_object_is_visible (obj_old);
visible_new = nmp_object_is_visible (obj_new);
if (!visible_old && visible_new) {
o = obj_new;
cache_op = NMP_CACHE_OPS_ADDED;
else if (was_visible && !is_visible) {
/* This is a bit ugly. The object was visible and changed in a way that it became invisible.
* We raise a removed signal, but contrary to a real 'remove', @obj_new is already changed to be
* different from what it was when the user saw it the last time.
*
* The more correct solution would be to have cache_pre_hook() create a clone of the original
* value before it was changed to become invisible.
*
* But, don't bother. Probably nobody depends on the original values and only cares about the
* id properties (which are still correct).
*/
} else if (visible_old && !visible_new) {
o = obj_old;
cache_op = NMP_CACHE_OPS_REMOVED;
} else if (!is_visible)
} else if (!visible_new) {
/* it was invisible and stayed invisible. Nothing to do. */
return;
} else
o = obj_new;
break;
case NMP_CACHE_OPS_REMOVED:
if (!was_visible)
if (!nmp_object_is_visible (obj_old))
return;
o = obj_old;
break;
default:
g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED);
nm_assert (cache_op == NMP_CACHE_OPS_UNCHANGED);
return;
}
klass = NMP_OBJECT_GET_CLASS (obj_new);
klass = NMP_OBJECT_GET_CLASS (o);
_LOGt ("emit signal %s %s: %s",
klass->signal_type,
nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op),
nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
/* don't expose @obj_new directly, but clone the public fields. A signal handler might
* call back into NMPlatform which could invalidate (or modify) @obj_new. */
memcpy (&obj_clone.object, &obj_new->object, klass->sizeof_public);
nmp_object_ref (o);
g_signal_emit (platform,
_nm_platform_signal_id_get (klass->signal_type_id),
0,
(int) klass->obj_type,
obj_clone.object.ifindex,
&obj_clone.object,
o->object.ifindex,
&o->object,
(int) cache_op);
nmp_object_unref (o);
}
/*****************************************************************************/
@ -3164,12 +3155,15 @@ static void
delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nmpobj NMPObject *obj_cache = NULL;
gboolean was_visible;
nm_auto_nmpobj const NMPObject *obj_old = NULL;
nm_auto_nmpobj const NMPObject *obj_new = NULL;
NMPCacheOpsType cache_op;
cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_old, &obj_new);
if (cache_op == NMP_CACHE_OPS_UNCHANGED)
return;
cache_on_change (platform, cache_op, obj_old, obj_new);
do_emit_signal (platform, cache_op, obj_old, obj_new);
}
static void
@ -3290,7 +3284,7 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink)
any = TRUE;
priv->delayed_action.is_handling--;
cache_prune_candidates_prune (platform);
cache_prune_all (platform);
return any;
}
@ -3353,101 +3347,69 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform,
/*****************************************************************************/
static void
cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type)
cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPCacheId cache_id;
priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache,
nmp_cache_id_init_object_type (&cache_id, obj_type, FALSE),
priv->prune_candidates);
_LOGt ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name,
priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0);
}
static void
cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj)
{
NMLinuxPlatformPrivate *priv;
if (!obj)
return;
priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
if (!priv->prune_candidates)
priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL);
if (_LOGt_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj))
_LOGt ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj));
}
static void
cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj)
{
NMLinuxPlatformPrivate *priv;
if (!obj)
return;
priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
if (priv->prune_candidates) {
if (_LOGt_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj))
_LOGt ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
g_hash_table_remove (priv->prune_candidates, obj);
}
}
static void
cache_prune_candidates_prune (NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
GHashTable *prune_candidates;
GHashTableIter iter;
NMDedupMultiIter iter;
const NMPObject *obj;
gboolean was_visible;
NMPCacheOpsType cache_op;
NMPLookup lookup;
if (!priv->prune_candidates)
return;
nmp_lookup_init_obj_type (&lookup,
obj_type,
FALSE);
nm_dedup_multi_iter_init (&iter,
nmp_cache_lookup (priv->cache,
&lookup));
while (nm_dedup_multi_iter_next (&iter)) {
if (iter.current->dirty) {
nm_auto_nmpobj const NMPObject *obj_old = NULL;
prune_candidates = priv->prune_candidates;
priv->prune_candidates = NULL;
g_hash_table_iter_init (&iter, prune_candidates);
while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) {
nm_auto_nmpobj NMPObject *obj_cache = NULL;
_LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
obj = iter.current->box->obj;
_LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_old);
nm_assert (cache_op == NMP_CACHE_OPS_REMOVED);
cache_on_change (platform, cache_op, obj_old, NULL);
do_emit_signal (platform, cache_op, obj_old, NULL);
}
}
g_hash_table_unref (prune_candidates);
}
static void
cache_pre_hook (NMPCache *cache, const NMPObject *obj_old, const NMPObject *obj_new, NMPCacheOpsType cache_op, gpointer user_data)
cache_prune_all (NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
DelayedActionType iflags, action_type;
action_type = DELAYED_ACTION_TYPE_REFRESH_ALL;
FOR_EACH_DELAYED_ACTION (iflags, action_type) {
bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)];
if (*p) {
*p = FALSE;
cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags));
}
}
}
static void
cache_on_change (NMPlatform *platform,
NMPCacheOpsType cache_op,
const NMPObject *obj_old,
const NMPObject *obj_new)
{
NMPlatform *platform = user_data;
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
const NMPClass *klass;
char str_buf[sizeof (_nm_utils_to_string_buffer)];
char str_buf2[sizeof (_nm_utils_to_string_buffer)];
nm_assert (obj_old || obj_new);
nm_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED));
nm_assert (cache_op != NMP_CACHE_OPS_ADDED || (obj_old == NULL && NMP_OBJECT_IS_VALID (obj_new) && nmp_object_is_alive (obj_new)));
nm_assert (cache_op != NMP_CACHE_OPS_REMOVED || (obj_new == NULL && NMP_OBJECT_IS_VALID (obj_old) && nmp_object_is_alive (obj_old)));
nm_assert (cache_op != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (obj_old) && nmp_object_is_alive (obj_old) && NMP_OBJECT_IS_VALID (obj_new) && nmp_object_is_alive (obj_new)));
nm_assert (obj_new == NULL || obj_old == NULL || nmp_object_id_equal (obj_new, obj_old));
nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_old) == NMP_OBJECT_GET_CLASS (obj_new));
ASSERT_nmp_cache_ops (priv->cache, cache_op, obj_old, obj_new);
if (cache_op == NMP_CACHE_OPS_UNCHANGED)
return;
klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new);
nm_assert (klass == (obj_new ? NMP_OBJECT_GET_CLASS (obj_new) : NMP_OBJECT_GET_CLASS (obj_old)));
_LOGt ("update-cache-%s: %s: %s%s%s",
klass->obj_type_name,
(cache_op == NMP_CACHE_OPS_UPDATED
@ -3478,7 +3440,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *obj_old, const NMPObject *obj_
{
/* check whether we are about to change a master link that needs toggling connected state. */
if ( obj_new /* <-- nonsensical, make coverity happy */
&& nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old))
&& nmp_cache_link_connected_needs_toggle (priv->cache, obj_new, obj_new, obj_old))
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex));
}
{
@ -3522,16 +3484,16 @@ cache_pre_hook (NMPCache *cache, const NMPObject *obj_old, const NMPObject *obj_
ifindex = obj_new->link.ifindex;
if (ifindex > 0) {
const NMPlatformLink *const *links;
NMPLookup lookup;
NMDedupMultiIter iter;
const NMPlatformLink *l;
links = cache_lookup_all_objects (NMPlatformLink, platform, NMP_OBJECT_TYPE_LINK, FALSE);
if (links) {
for (; *links; links++) {
const NMPlatformLink *l = (*links);
if (l->parent == ifindex)
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex));
}
nmp_lookup_init_link (&lookup, FALSE);
nmp_cache_iter_for_each_link (&iter,
nmp_cache_lookup (priv->cache, &lookup),
&l) {
if (l->parent == ifindex)
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex));
}
}
}
@ -3664,14 +3626,12 @@ static void
cache_post (NMPlatform *platform,
struct nlmsghdr *msghdr,
NMPCacheOpsType cache_op,
NMPObject *obj,
NMPObject *obj_cache)
const NMPObject *obj,
const NMPObject *obj_old,
const NMPObject *obj_new)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_assert (NMP_OBJECT_IS_VALID (obj));
nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache));
if (msghdr->nlmsg_type == RTM_NEWROUTE) {
DelayedActionType action_type;
@ -3739,8 +3699,13 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha
_LOGD ("do-request-link: %d %s", ifindex, name ? name : "");
if (ifindex > 0) {
cache_prune_candidates_record_one (platform,
(NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex));
const NMDedupMultiEntry *entry;
entry = nmp_cache_lookup_entry_link (priv->cache, ifindex);
if (entry) {
priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE;
nm_dedup_multi_entry_set_dirty (entry, TRUE);
}
}
event_handler_read_netlink (platform, FALSE);
@ -3772,7 +3737,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL;
FOR_EACH_DELAYED_ACTION (iflags, action_type) {
cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags));
priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE;
nmp_cache_dirty_set_all (priv->cache,
delayed_action_refresh_to_object_type (iflags));
}
FOR_EACH_DELAYED_ACTION (iflags, action_type) {
@ -3898,12 +3865,10 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nmpobj NMPObject *obj = NULL;
nm_auto_nmpobj NMPObject *obj_cache = NULL;
NMPCacheOpsType cache_op;
struct nlmsghdr *msghdr;
char buf_nlmsg_type[16];
gboolean id_only = FALSE;
gboolean was_visible;
msghdr = nlmsg_hdr (msg);
@ -3932,31 +3897,36 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
msghdr->nlmsg_seq, nmp_object_to_string (obj,
id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
switch (msghdr->nlmsg_type) {
{
nm_auto_nmpobj const NMPObject *obj_old = NULL;
nm_auto_nmpobj const NMPObject *obj_new = NULL;
case RTM_NEWLINK:
case RTM_NEWADDR:
case RTM_NEWROUTE:
case RTM_GETLINK:
cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform);
switch (msghdr->nlmsg_type) {
cache_post (platform, msghdr, cache_op, obj, obj_cache);
case RTM_NEWLINK:
case RTM_NEWADDR:
case RTM_NEWROUTE:
case RTM_GETLINK:
cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_old, &obj_new);
cache_on_change (platform, cache_op, obj_old, obj_new);
cache_post (platform, msghdr, cache_op, obj, obj_old, obj_new);
do_emit_signal (platform, cache_op, obj_old, obj_new);
break;
do_emit_signal (platform, obj_cache, cache_op, was_visible);
break;
case RTM_DELLINK:
case RTM_DELADDR:
case RTM_DELROUTE:
cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_old, &obj_new);
if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
cache_on_change (platform, cache_op, obj_old, obj_new);
do_emit_signal (platform, cache_op, obj_old, obj_new);
}
break;
case RTM_DELLINK:
case RTM_DELADDR:
case RTM_DELROUTE:
cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
break;
default:
break;
default:
break;
}
}
cache_prune_candidates_drop (platform, obj_cache);
}
/*****************************************************************************/
@ -3973,25 +3943,15 @@ cache_lookup_link (NMPlatform *platform, int ifindex)
return obj_cache;
}
const NMPlatformObject *const*
nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len)
{
g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL);
g_return_val_if_fail (cache_id, NULL);
return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
cache_id, out_len);
}
static GArray *
link_get_all (NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPCacheId cache_id;
NMPLookup lookup;
return nmp_cache_lookup_multi_to_array (priv->cache,
NMP_OBJECT_TYPE_LINK,
nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, TRUE));
nmp_lookup_init_link (&lookup, TRUE);
return nmp_cache_lookup_to_array (nmp_cache_lookup (priv->cache, &lookup),
NMP_OBJECT_TYPE_LINK);
}
static const NMPlatformLink *
@ -5733,10 +5693,9 @@ static gboolean
link_can_assume (NMPlatform *platform, int ifindex)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPCacheId cache_id;
const NMPlatformObject *const *objs;
guint i, len;
const NMPObject *link;
NMPLookup lookup;
const NMPObject *link, *o;
NMDedupMultiIter iter;
if (ifindex <= 0)
return FALSE;
@ -5751,21 +5710,23 @@ link_can_assume (NMPlatform *platform, int ifindex)
if (link->link.master > 0)
return TRUE;
if (nmp_cache_lookup_multi (priv->cache,
nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex),
NULL))
nmp_lookup_init_addrroute (&lookup,
NMP_OBJECT_TYPE_IP4_ADDRESS,
ifindex,
TRUE);
if (nmp_cache_lookup (priv->cache, &lookup))
return TRUE;
objs = nmp_cache_lookup_multi (priv->cache,
nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex),
&len);
if (objs) {
for (i = 0; i < len; i++) {
const NMPlatformIP6Address *a = (NMPlatformIP6Address *) objs[i];
if (!IN6_IS_ADDR_LINKLOCAL (&a->address))
nmp_lookup_init_addrroute (&lookup,
NMP_OBJECT_TYPE_IP6_ADDRESS,
ifindex,
TRUE);
nmp_cache_iter_for_each (&iter,
nmp_cache_lookup (priv->cache, &lookup),
&o) {
nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ADDRESS);
if (!IN6_IS_ADDR_LINKLOCAL (&o->ip6_address.address))
return TRUE;
}
}
return FALSE;
}
@ -5844,15 +5805,15 @@ static GArray *
ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPCacheId cache_id;
NMPLookup lookup;
nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS));
return nmp_cache_lookup_multi_to_array (priv->cache,
obj_type,
nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id,
obj_type,
ifindex));
nmp_lookup_init_addrroute (&lookup,
obj_type,
ifindex,
TRUE);
return nmp_cache_lookup_to_array (nmp_cache_lookup (priv->cache, &lookup),
obj_type);
}
static GArray *
@ -6010,12 +5971,13 @@ static GArray *
ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPCacheId cache_id;
const NMPlatformIPRoute *const* routes;
NMDedupMultiIter iter;
NMPLookup lookup;
const NMDedupMultiHeadEntry *head_entry;
GArray *array;
const NMPClass *klass;
const NMPObject *o;
gboolean with_rtprot_kernel;
guint i, len;
nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
@ -6024,23 +5986,24 @@ ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NM
klass = nmp_class_from_type (obj_type);
nmp_cache_id_init_routes_visible (&cache_id,
obj_type,
NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT),
NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT),
ifindex);
head_entry = nmp_cache_lookup (priv->cache,
nmp_lookup_init_route_visible (&lookup,
obj_type,
ifindex,
NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT),
NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)));
routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len);
array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len);
array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, head_entry ? head_entry->len : 0);
with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL);
for (i = 0; i < len; i++) {
nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass);
nmp_cache_iter_for_each (&iter,
head_entry,
&o) {
nm_assert (NMP_OBJECT_GET_CLASS (o) == klass);
if ( with_rtprot_kernel
|| routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
g_array_append_vals (array, routes[i], 1);
|| o->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
g_array_append_vals (array, &o->ip_route, 1);
}
return array;
}
@ -6634,18 +6597,19 @@ cache_update_link_udev (NMPlatform *platform,
struct udev_device *udevice)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nmpobj NMPObject *obj_cache = NULL;
gboolean was_visible;
nm_auto_nmpobj const NMPObject *obj_old = NULL;
nm_auto_nmpobj const NMPObject *obj_new = NULL;
NMPCacheOpsType cache_op;
cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform);
cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_old, &obj_new);
if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
nm_auto_pop_netns NMPNetns *netns = NULL;
cache_on_change (platform, cache_op, obj_old, obj_new);
if (!nm_platform_netns_push (platform, &netns))
return;
do_emit_signal (platform, obj_cache, cache_op, was_visible);
do_emit_signal (platform, cache_op, obj_old, obj_new);
}
}
@ -6753,19 +6717,15 @@ static void
nm_linux_platform_init (NMLinuxPlatform *self)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self);
gboolean use_udev;
use_udev = nmp_netns_is_initial ()
&& access ("/sys", W_OK) == 0;
priv->nlh_seq_next = 1;
priv->cache = nmp_cache_new (use_udev);
priv->delayed_action.list_master_connected = g_ptr_array_new ();
priv->delayed_action.list_refresh_link = g_ptr_array_new ();
priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
if (use_udev) {
if ( nmp_netns_is_initial ()
&& access ("/sys", W_OK) == 0) {
priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL },
handle_udev_event, self);
}
@ -6782,6 +6742,9 @@ constructed (GObject *_object)
nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ());
priv->cache = nmp_cache_new (nm_platform_get_multi_idx (platform),
priv->udev_client != NULL);
_LOGD ("create (%s netns, %s, %s udev)",
!platform->_netns ? "ignore" : "use",
!platform->_netns && nmp_netns_is_initial ()
@ -6892,8 +6855,6 @@ dispose (GObject *object)
g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0);
g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0);
g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
}

View file

@ -39,10 +39,4 @@ NMPlatform *nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support
void nm_linux_platform_setup (void);
struct _NMPCacheId;
const NMPlatformObject *const *nm_linux_platform_lookup (NMPlatform *platform,
const struct _NMPCacheId *cache_id,
guint *out_len);
#endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */

View file

@ -4149,6 +4149,39 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
return c < 0 ? -1 : 1; \
} G_STMT_END
guint
nm_platform_link_hash (const NMPlatformLink *obj)
{
guint h = 99413953;
guint8 i8;
h = NM_HASH_COMBINE (h, obj->ifindex);
h = NM_HASH_COMBINE (h, obj->type);
h = NM_HASH_COMBINE (h, g_str_hash (obj->name));
h = NM_HASH_COMBINE (h, obj->master);
h = NM_HASH_COMBINE (h, obj->parent);
h = NM_HASH_COMBINE (h, obj->n_ifi_flags);
h = NM_HASH_COMBINE (h, obj->connected);
h = NM_HASH_COMBINE (h, obj->mtu);
h = NM_HASH_COMBINE (h, !!obj->initialized);
h = NM_HASH_COMBINE (h, obj->arptype);
h = NM_HASH_COMBINE (h, obj->addr.len);
h = NM_HASH_COMBINE (h, obj->inet6_addr_gen_mode_inv);
if (obj->kind)
h = NM_HASH_COMBINE (h, g_str_hash (obj->kind));
if (obj->driver)
h = NM_HASH_COMBINE (h, g_str_hash (obj->driver));
for (i8 = 0; i8 < obj->addr.len; i8++)
h = NM_HASH_COMBINE (h, obj->addr.data[i8]);
for (i8 = 0; i8 < sizeof (obj->inet6_token); i8++)
h = NM_HASH_COMBINE (h, obj->inet6_token.id_u8[i8]);
h = NM_HASH_COMBINE (h, obj->rx_packets);
h = NM_HASH_COMBINE (h, obj->rx_bytes);
h = NM_HASH_COMBINE (h, obj->tx_packets);
h = NM_HASH_COMBINE (h, obj->tx_bytes);
return h;
}
int
nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
{

View file

@ -1024,6 +1024,7 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
return nm_platform_ip6_route_cmp_full (a, b, TRUE);
}
guint nm_platform_link_hash (const NMPlatformLink *obj);
guint nm_platform_ip4_address_hash (const NMPlatformIP4Address *obj);
guint nm_platform_ip6_address_hash (const NMPlatformIP6Address *obj);
guint nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj);

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,6 @@
#include "nm-utils/nm-obj.h"
#include "nm-utils/nm-dedup-multi.h"
#include "nm-platform.h"
#include "nm-multi-index.h"
struct udev_device;
@ -60,7 +59,14 @@ typedef enum { /*< skip >*/
typedef enum { /*< skip >*/
NMP_CACHE_ID_TYPE_NONE,
/* all the objects of a certain type */
/* all the objects of a certain type.
*
* This index is special. It is the only one that contains *all* object.
* Other indexes may consider some object as non "partitionable", hence
* they don't track all objects.
*
* Hence, this index type is used when looking at all objects (still
* partitioned by type). */
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
/* index for the link objects by ifname. */
@ -76,7 +82,7 @@ typedef enum { /*< skip >*/
/* all the visible addresses/routes (by object-type) for an ifindex. */
NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX,
/* three indeces for the visible routes, per ifindex. */
/* indeces for the visible routes, per ifindex. */
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT,
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT,
@ -95,50 +101,6 @@ typedef enum { /*< skip >*/
NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
} NMPCacheIdType;
typedef struct _NMPCacheId NMPCacheId;
struct _NMPCacheId {
union {
NMMultiIndexId base;
guint8 _id_type; /* NMPCacheIdType as guint8 */
struct _nm_packed {
/* NMP_CACHE_ID_TYPE_OBJECT_TYPE */
/* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */
guint8 _id_type;
guint8 obj_type; /* NMPObjectType as guint8 */
} object_type;
struct _nm_packed {
/* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */
guint8 _id_type;
guint8 obj_type; /* NMPObjectType as guint8 */
int _misaligned_ifindex;
} object_type_by_ifindex;
struct _nm_packed {
/* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */
guint8 _id_type;
char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */
} link_by_ifname;
struct _nm_packed {
/* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4 */
guint8 _id_type;
guint8 plen;
guint32 _misaligned_metric;
guint32 _misaligned_network;
} routes_by_destination_ip4;
struct _nm_packed {
/* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6 */
guint8 _id_type;
guint8 plen;
guint32 _misaligned_metric;
struct in6_addr _misaligned_network;
} routes_by_destination_ip6;
};
};
typedef struct {
NMDedupMultiObjClass parent;
const char *obj_type_name;
@ -155,10 +117,6 @@ typedef struct {
/* Only for NMPObjectLnk* types. */
NMLinkType lnk_link_type;
/* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index,
* initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */
gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id);
guint (*cmd_obj_hash) (const NMPObject *obj);
int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2);
void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src);
@ -276,7 +234,6 @@ struct _NMPObject {
const NMPClass *_class;
};
guint _ref_count;
bool is_cached;
union {
NMPlatformObject object;
@ -387,8 +344,8 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
const NMPClass *nmp_class_from_type (NMPObjectType obj_type);
NMPObject *nmp_object_ref (NMPObject *object);
void nmp_object_unref (NMPObject *object);
const NMPObject *nmp_object_ref (const NMPObject *object);
void nmp_object_unref (const NMPObject *object);
NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob);
NMPObject *nmp_object_new_link (int ifindex);
@ -411,13 +368,13 @@ guint nmp_object_id_hash (const NMPObject *obj);
gboolean nmp_object_is_alive (const NMPObject *obj);
gboolean nmp_object_is_visible (const NMPObject *obj);
void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev);
void _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev);
#define nm_auto_nmpobj __attribute__((cleanup(_nm_auto_nmpobj_cleanup)))
static inline void
_nm_auto_nmpobj_cleanup (NMPObject **pobj)
_nm_auto_nmpobj_cleanup (gpointer p)
{
nmp_object_unref (*pobj);
nmp_object_unref (*((const NMPObject **) p));
}
typedef struct _NMPCache NMPCache;
@ -425,23 +382,90 @@ typedef struct _NMPCache NMPCache;
typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data);
gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b);
guint nmp_cache_id_hash (const NMPCacheId *id);
NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id);
void nmp_cache_id_destroy (NMPCacheId *id);
const NMDedupMultiEntry *nmp_cache_lookup_entry_link (const NMPCache *cache, int ifindex);
NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only);
NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex);
NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex);
NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname);
NMPCacheId *nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, guint32 network, guint8 plen, guint32 metric);
NMPCacheId *nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, const struct in6_addr *network, guint8 plen, guint32 metric);
const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len);
GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id);
const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj);
const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex);
typedef struct {
NMPCacheIdType cache_id_type;
NMPObject selector_obj;
} NMPLookup;
const NMDedupMultiHeadEntry *nmp_cache_lookup_all (const NMPCache *cache,
NMPCacheIdType cache_id_type,
const NMPObject *select_obj);
static inline const NMDedupMultiHeadEntry *
nmp_cache_lookup (const NMPCache *cache,
const NMPLookup *lookup)
{
return nmp_cache_lookup_all (cache, lookup->cache_id_type, &lookup->selector_obj);
}
const NMPLookup *nmp_lookup_init_obj_type (NMPLookup *lookup,
NMPObjectType obj_type,
gboolean visible_only);
const NMPLookup *nmp_lookup_init_link (NMPLookup *lookup,
gboolean visible_only);
const NMPLookup *nmp_lookup_init_link_by_ifname (NMPLookup *lookup,
const char *ifname);
const NMPLookup *nmp_lookup_init_addrroute (NMPLookup *lookup,
NMPObjectType obj_type,
int ifindex,
gboolean visible_only);
const NMPLookup *nmp_lookup_init_route_visible (NMPLookup *lookup,
NMPObjectType obj_type,
int ifindex,
gboolean with_default,
gboolean with_non_default);
const NMPLookup *nmp_lookup_init_route_by_dest (NMPLookup *lookup,
int addr_family,
gconstpointer network,
guint plen,
guint32 metric);
GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry,
NMPObjectType obj_type);
static inline gboolean
nmp_cache_iter_next (NMDedupMultiIter *iter, const NMPObject **out_obj)
{
gboolean has_next;
has_next = nm_dedup_multi_iter_next (iter);
if (has_next) {
nm_assert (NMP_OBJECT_IS_VALID (iter->current->box->obj));
NM_SET_OUT (out_obj, iter->current->box->obj);
}
return has_next;
}
static inline gboolean
nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj)
{
gboolean has_next;
has_next = nm_dedup_multi_iter_next (iter);
if (has_next) {
nm_assert (NMP_OBJECT_GET_TYPE (iter->current->box->obj) == NMP_OBJECT_TYPE_LINK);
NM_SET_OUT (out_obj, &(((const NMPObject *) iter->current->box->obj)->link));
}
return has_next;
}
#define nmp_cache_iter_for_each(iter, head, obj) \
for (nm_dedup_multi_iter_init ((iter), \
(head)); \
nmp_cache_iter_next ((iter), (obj)); \
)
#define nmp_cache_iter_for_each_link(iter, head, obj) \
for (nm_dedup_multi_iter_init ((iter), \
(head)); \
nmp_cache_iter_next_link ((iter), (obj)); \
)
const NMPObject *nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route);
const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache,
@ -451,9 +475,6 @@ const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache,
NMLinkType link_type,
NMPObjectMatchFn match_fn,
gpointer user_data);
GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache,
NMPCacheId *cache_id,
GHashTable *hash);
gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave);
const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave);
@ -462,13 +483,71 @@ gboolean nmp_cache_use_udev_get (const NMPCache *cache);
void ASSERT_nmp_cache_is_consistent (const NMPCache *cache);
NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_remove (NMPCache *cache,
const NMPObject *obj_needle,
gboolean equals_by_ptr,
const NMPObject **out_obj_old);
NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache,
const NMPObject *obj_needle,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new);
NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache,
NMPObject *obj,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new);
NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache,
int ifindex,
struct udev_device *udevice,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new);
NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache,
int ifindex,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new);
NMPCache *nmp_cache_new (gboolean use_udev);
void nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type);
NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev);
void nmp_cache_free (NMPCache *cache);
static inline void
ASSERT_nmp_cache_ops (const NMPCache *cache,
NMPCacheOpsType ops_type,
const NMPObject *obj_old,
const NMPObject *obj_new)
{
#if NM_MORE_ASSERTS
nm_assert (cache);
nm_assert (obj_old || obj_new);
nm_assert (!obj_old || ( NMP_OBJECT_IS_VALID (obj_old)
&& !NMP_OBJECT_IS_STACKINIT (obj_old)
&& nmp_object_is_alive (obj_old)));
nm_assert (!obj_new || ( NMP_OBJECT_IS_VALID (obj_new)
&& !NMP_OBJECT_IS_STACKINIT (obj_new)
&& nmp_object_is_alive (obj_new)));
switch (ops_type) {
case NMP_CACHE_OPS_UNCHANGED:
nm_assert (obj_old == obj_new);
break;
case NMP_CACHE_OPS_ADDED:
nm_assert (!obj_old && obj_new);
break;
case NMP_CACHE_OPS_UPDATED:
nm_assert (obj_old && obj_new && obj_old != obj_new);
break;
case NMP_CACHE_OPS_REMOVED:
nm_assert (obj_old && !obj_new);
break;
default:
nm_assert_not_reached ();
}
nm_assert (obj_new == NULL || obj_old == NULL || nmp_object_id_equal (obj_new, obj_old));
nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_old) == NMP_OBJECT_GET_CLASS (obj_new));
nm_assert (obj_new == nmp_cache_lookup_obj (cache, obj_new ?: obj_old));
#endif
}
#endif /* __NMP_OBJECT_H__ */

View file

@ -103,150 +103,141 @@ _nmp_object_equal (const NMPObject *a, const NMPObject *b)
/*****************************************************************************/
static void
_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains)
_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMDedupMultiHeadEntry *head_entry, const NMPObject *obj, gboolean contains)
{
const NMPlatformObject *const *objects;
guint i, len;
NMDedupMultiIter iter;
gboolean found;
guint i, len;
const NMPObject *o;
g_assert (cache_id);
g_assert (NMP_OBJECT_IS_VALID (obj));
g_assert (nmp_cache_lookup_obj (cache, obj) == obj);
g_assert (!head_entry || (head_entry->len > 0 && c_list_length (&head_entry->lst_entries_head) == head_entry->len));
objects = nmp_cache_lookup_multi (cache, cache_id, &len);
g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len]));
len = head_entry ? head_entry->len : 0;
found = FALSE;
for (i = 0; i < len; i++) {
NMPObject *o;
g_assert (objects[i]);
o = NMP_OBJECT_UP_CAST (objects[i]);
i = 0;
nmp_cache_iter_for_each (&iter,
head_entry,
&o) {
g_assert (NMP_OBJECT_IS_VALID (o));
if (obj == o) {
g_assert (!found);
found = TRUE;
}
i++;
}
g_assert (len == i);
g_assert (!!contains == found);
}
static void
_assert_cache_multi_lookup_contains_link (const NMPCache *cache,
gboolean visible_only,
const NMPObject *obj,
gboolean contains)
{
const NMDedupMultiHeadEntry *head_entry;
NMPLookup lookup;
g_assert (cache);
nmp_lookup_init_link (&lookup, visible_only);
head_entry = nmp_cache_lookup (cache, &lookup);
_assert_cache_multi_lookup_contains (cache, head_entry, obj, contains);
}
/*****************************************************************************/
typedef struct {
NMPCache *cache;
NMPCacheOpsType expected_ops_type;
const NMPObject *obj_clone;
NMPObject *new_clone;
gboolean was_visible;
gboolean called;
} _NMPCacheUpdateData;
static void
_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data)
ops_post_check (NMPCache *cache,
NMPCacheOpsType ops_type,
const NMPObject *obj_old,
const NMPObject *obj_new,
const NMPObject *obj_new_expected,
NMPCacheOpsType expected_ops_type)
{
_NMPCacheUpdateData *data = user_data;
g_assert (cache);
g_assert (data);
g_assert (!data->called);
g_assert (data->cache == cache);
g_assert_cmpint (data->expected_ops_type, ==, ops_type);
g_assert_cmpint (expected_ops_type, ==, ops_type);
switch (ops_type) {
case NMP_CACHE_OPS_ADDED:
g_assert (!old);
g_assert (NMP_OBJECT_IS_VALID (new));
g_assert (nmp_object_is_alive (new));
g_assert (nmp_object_id_equal (data->obj_clone, new));
g_assert (nmp_object_equal (data->obj_clone, new));
g_assert (!obj_old);
g_assert (NMP_OBJECT_IS_VALID (obj_new));
g_assert (nmp_object_is_alive (obj_new));
g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
g_assert (nmp_object_equal (obj_new_expected, obj_new));
break;
case NMP_CACHE_OPS_UPDATED:
g_assert (NMP_OBJECT_IS_VALID (old));
g_assert (NMP_OBJECT_IS_VALID (new));
g_assert (nmp_object_is_alive (old));
g_assert (nmp_object_is_alive (new));
g_assert (nmp_object_id_equal (data->obj_clone, new));
g_assert (nmp_object_id_equal (data->obj_clone, old));
g_assert (nmp_object_id_equal (old, new));
g_assert (nmp_object_equal (data->obj_clone, new));
g_assert (!nmp_object_equal (data->obj_clone, old));
g_assert (!nmp_object_equal (old, new));
g_assert (obj_old != obj_new);
g_assert (NMP_OBJECT_IS_VALID (obj_old));
g_assert (NMP_OBJECT_IS_VALID (obj_new));
g_assert (nmp_object_is_alive (obj_old));
g_assert (nmp_object_is_alive (obj_new));
g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
g_assert (nmp_object_id_equal (obj_new_expected, obj_old));
g_assert (nmp_object_id_equal (obj_old, obj_new));
g_assert (nmp_object_equal (obj_new_expected, obj_new));
g_assert (!nmp_object_equal (obj_new_expected, obj_old));
g_assert (!nmp_object_equal (obj_old, obj_new));
break;
case NMP_CACHE_OPS_REMOVED:
g_assert (!new);
g_assert (NMP_OBJECT_IS_VALID (old));
g_assert (nmp_object_is_alive (old));
g_assert (nmp_object_id_equal (data->obj_clone, old));
g_assert (!obj_new);
g_assert (NMP_OBJECT_IS_VALID (obj_old));
g_assert (nmp_object_is_alive (obj_old));
if (obj_new_expected)
g_assert (nmp_object_id_equal (obj_new_expected, obj_old));
break;
case NMP_CACHE_OPS_UNCHANGED:
g_assert (obj_old == obj_new);
if (obj_old) {
g_assert (NMP_OBJECT_IS_VALID (obj_old));
g_assert (nmp_object_is_alive (obj_old));
g_assert (nmp_object_equal (obj_old, obj_new));
g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
} else
g_assert (!obj_new_expected);
break;
default:
g_assert_not_reached ();
}
data->was_visible = old ? nmp_object_is_visible (old) : FALSE;
data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL;
data->called = TRUE;
}
static void
_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type)
_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **out_obj_old, const NMPObject **out_obj_new, NMPCacheOpsType expected_ops_type)
{
NMPCacheOpsType ops_type;
NMPObject *obj2;
gboolean was_visible;
nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE);
nm_auto_nmpobj NMPObject *new_clone = NULL;
const NMPObject *obj_prev;
const NMPObject *obj_old;
_NMPCacheUpdateData data = {
.cache = cache,
.expected_ops_type = expected_ops_type,
.obj_clone = obj_clone,
};
obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex);
if (obj_old && obj_old->_link.udev.device)
obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device);
_nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache));
const NMPObject *obj_new;
nm_auto_nmpobj NMPObject *obj_new_expected = NULL;
g_assert (cache);
g_assert (NMP_OBJECT_IS_VALID (obj));
ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data);
obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex);
obj_new_expected = nmp_object_clone (obj, FALSE);
if (obj_prev && obj_prev->_link.udev.device)
obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device);
_nmp_object_fixup_link_udev_fields (&obj_new_expected, NULL, nmp_cache_use_udev_get (cache));
new_clone = data.new_clone;
ops_type = nmp_cache_update_netlink (cache, obj, &obj_old, &obj_new);
ops_post_check (cache, ops_type, obj_old, obj_new,
nmp_object_is_alive (obj_new_expected) ? obj_new_expected : NULL,
expected_ops_type);
g_assert_cmpint (ops_type, ==, expected_ops_type);
if (ops_type != NMP_CACHE_OPS_UNCHANGED) {
g_assert (NMP_OBJECT_IS_VALID (obj2));
g_assert (data.called);
g_assert_cmpint (data.was_visible, ==, was_visible);
if (ops_type == NMP_CACHE_OPS_REMOVED)
g_assert (!data.new_clone);
else {
g_assert (data.new_clone);
g_assert (nmp_object_equal (obj2, data.new_clone));
}
} else {
g_assert (!data.called);
g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2));
}
g_assert (!obj2 || nmp_object_id_equal (obj, obj2));
if (ops_type != NMP_CACHE_OPS_REMOVED && obj2)
g_assert (nmp_object_equal (obj, obj2));
if (out_obj)
*out_obj = obj2;
if (out_obj_new)
*out_obj_new = obj_new;
else
nmp_object_unref (obj2);
if (out_was_visible)
*out_was_visible = was_visible;
nmp_object_unref (obj_new);
if (out_obj_old)
*out_obj_old = obj_old;
else
nmp_object_unref (obj_old);
}
static const NMPlatformLink pl_link_2 = {
@ -265,168 +256,189 @@ static void
test_cache_link (void)
{
NMPCache *cache;
NMPObject *obj1, *obj2;
NMPObject *objm1;
const NMPObject *obj_old, *obj_new;
NMPObject objs1;
gboolean was_visible;
NMPCacheId cache_id_storage;
struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0);
struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
NMPCacheOpsType ops_type;
nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL;
cache = nmp_cache_new (nmtst_get_rand_int () % 2);
multi_idx = nm_dedup_multi_index_new ();
cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2);
/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1);
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (NMP_OBJECT_UP_CAST (&objm1->object) == objm1);
g_assert (!nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (!obj2);
g_assert (!was_visible);
g_assert (!nmp_cache_lookup_obj (cache, obj1));
g_assert (!obj_old);
g_assert (!obj_new);
g_assert (!nmp_cache_lookup_obj (cache, objm1));
g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
nmp_object_unref (obj1);
nmp_object_unref (objm1);
/* Only when setting @is_in_netlink the link is added. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
objm1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
g_assert (!obj_old);
g_assert (obj_new);
g_assert (objm1 == obj_new);
g_assert (nmp_object_equal (objm1, obj_new));
g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
g_assert (nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
nmp_object_unref (objm1);
nmp_object_unref (obj_new);
/* updating the same link with identical value, has no effect. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
objm1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
g_assert (obj_old);
g_assert (obj_new);
g_assert (obj_new != objm1);
g_assert (nmp_object_equal (objm1, obj_new));
g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
nmp_object_unref (objm1);
nmp_object_unref (obj_new);
nmp_object_unref (obj_new);
/* remove the link from netlink */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (!nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (was_visible);
g_assert (!nmp_cache_lookup_obj (cache, obj1));
g_assert (obj_old);
g_assert (!obj_new);
g_assert (!nmp_cache_lookup_obj (cache, objm1));
g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
nmp_object_unref (obj1);
nmp_object_unref (obj2);
nmp_object_unref (objm1);
nmp_object_unref (obj_old);
nmp_object_unref (obj_new);
if (udev_device_2) {
/* now add the link only with aspect UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL);
ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new);
ASSERT_nmp_cache_is_consistent (cache);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (!nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
nmp_object_unref (obj2);
g_assert (!obj_old);
g_assert (obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
g_assert (!nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
nmp_object_unref (obj_new);
}
/* add it in netlink too. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
objm1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
if (udev_device_2) {
g_assert (obj_old);
g_assert (!nmp_object_is_visible (obj_old));
} else
g_assert (!obj_old);
g_assert (nmp_object_equal (objm1, obj_new));
g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
g_assert (nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
nmp_object_unref (objm1);
nmp_object_unref (obj_old);
nmp_object_unref (obj_new);
/* remove again from netlink. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = FALSE;
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
objm1->_link.netlink.is_in_netlink = FALSE;
g_assert (!nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (was_visible);
if (udev_device_2)
g_assert (obj_new == objm1);
else
g_assert (!obj_new);
g_assert (obj_old);
g_assert (nmp_object_is_alive (obj_old));
if (udev_device_2) {
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (!nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
g_assert (!nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
} else {
g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL);
g_assert (nmp_cache_lookup_obj (cache, objm1) == NULL);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL);
g_assert (nmp_object_is_visible (obj2));
g_assert (nmp_object_is_visible (obj_new));
}
nmp_object_unref (obj1);
nmp_object_unref (obj2);
nmp_object_unref (objm1);
nmp_object_unref (obj_old);
nmp_object_unref (obj_new);
/* now another link only with aspect UDEV. */
if (udev_device_3) {
/* now add the link only with aspect UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL);
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (NMP_OBJECT_IS_VALID (obj2));
g_assert (!was_visible);
g_assert (!nmp_object_is_visible (obj2));
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE);
g_assert_cmpint (obj2->link.initialized, ==, FALSE);
nmp_object_unref (obj2);
g_assert (NMP_OBJECT_IS_VALID (obj_new));
g_assert (!obj_old);
g_assert (!nmp_object_is_visible (obj_new));
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, FALSE);
g_assert_cmpint (obj_new->link.initialized, ==, FALSE);
nmp_object_unref (obj_new);
/* add it in netlink too. */
obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED);
objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3);
objm1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (objm1));
_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj2->link.initialized, ==, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
g_assert (obj_old);
g_assert (obj_new == objm1);
g_assert (nmp_object_equal (objm1, obj_new));
g_assert (!obj_old || !nmp_object_is_visible (obj_old));
g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
g_assert (nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj_new->link.initialized, ==, TRUE);
nmp_object_unref (objm1);
nmp_object_unref (obj_old);
nmp_object_unref (obj_new);
/* remove UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL);
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (was_visible);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache));
nmp_object_unref (obj2);
g_assert (obj_old && nmp_object_is_visible (obj_old));
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
g_assert (nmp_object_is_visible (obj_new));
_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj_new->link.initialized, ==, !nmp_cache_use_udev_get (cache));
nmp_object_unref (obj_new);
nmp_object_unref (obj_old);
}
nmp_cache_free (cache);