/* add the monitor mkring to the list of monitors of kring.
 * If this is the first monitor, intercept the callbacks
 */
static int
netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int zcopy)
{
    int error = 0;

    /* sinchronize with concurrently running nm_sync()s */
    nm_kr_get(kring);
    /* make sure the monitor array exists and is big enough */
    error = nm_monitor_alloc(kring, kring->n_monitors + 1);
    if (error)
        goto out;
    kring->monitors[kring->n_monitors] = mkring;
    mkring->mon_pos = kring->n_monitors;
    kring->n_monitors++;
    if (kring->n_monitors == 1) {
        /* this is the first monitor, intercept callbacks */
        D("%s: intercept callbacks on %s", mkring->name, kring->name);
        kring->mon_sync = kring->nm_sync;
        /* zcopy monitors do not override nm_notify(), but
         * we save the original one regardless, so that
         * netmap_monitor_del() does not need to know the
         * monitor type
         */
        kring->mon_notify = kring->nm_notify;
        if (kring->tx == NR_TX) {
            kring->nm_sync = (zcopy ? netmap_zmon_parent_txsync :
                              netmap_monitor_parent_txsync);
        } else {
            kring->nm_sync = (zcopy ? netmap_zmon_parent_rxsync :
                              netmap_monitor_parent_rxsync);
            if (!zcopy) {
                /* also intercept notify */
                kring->nm_notify = netmap_monitor_parent_notify;
                kring->mon_tail = kring->nr_hwtail;
            }
        }
    }

out:
    nm_kr_put(kring);
    return error;
}
/* add the monitor mkring to the list of monitors of kring.
 * If this is the first monitor, intercept the callbacks
 */
static int
netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int zmon)
{
	int error = NM_IRQ_COMPLETED;
	enum txrx t = kring->tx;
	struct netmap_zmon_list *z = &kring->zmon_list[t];
	struct netmap_zmon_list *mz = &mkring->zmon_list[t];

	/* a zero-copy monitor which is not the first in the list
	 * must monitor the previous monitor
	 */
	if (zmon && z->prev != NULL)
		kring = z->prev;

	/* sinchronize with concurrently running nm_sync()s */
	nm_kr_stop(kring, NM_KR_LOCKED);

	if (nm_monitor_none(kring)) {
		/* this is the first monitor, intercept callbacks */
		ND("intercept callbacks on %s", kring->name);
		kring->mon_sync = kring->nm_sync;
		kring->mon_notify = kring->nm_notify;
		if (kring->tx == NR_TX) {
			kring->nm_sync = netmap_monitor_parent_txsync;
		} else {
			kring->nm_sync = netmap_monitor_parent_rxsync;
			kring->nm_notify = netmap_monitor_parent_notify;
			kring->mon_tail = kring->nr_hwtail;
		}
	}

	if (zmon) {
		/* append the zmon to the list */
		struct netmap_monitor_adapter *mna =
			(struct netmap_monitor_adapter *)mkring->na;
		struct netmap_adapter *pna;

		if (z->prev != NULL)
			z->prev->zmon_list[t].next = mkring;
		mz->prev = z->prev;
		z->prev = mkring;
		if (z->next == NULL)
			z->next = mkring;

		/* grap a reference to the previous netmap adapter
		 * in the chain (this may be the monitored port
		 * or another zero-copy monitor)
		 */
		pna = kring->na;
		netmap_adapter_get(pna);
		netmap_adapter_put(mna->priv.np_na);
		mna->priv.np_na = pna;
	} else {
		/* make sure the monitor array exists and is big enough */
		error = nm_monitor_alloc(kring, kring->n_monitors + 1);
		if (error)
			goto out;
		kring->monitors[kring->n_monitors] = mkring;
		mkring->mon_pos[kring->tx] = kring->n_monitors;
		kring->n_monitors++;
	}

out:
	nm_kr_start(kring);
	return error;
}