static void destroy(const struct xt_tgdtor_param *par) { struct xt_MPLS_target_info *mplsinfo = par->targinfo; MPLS_ENTER; if (mplsinfo->nhlfe) mpls_nhlfe_release(mplsinfo->nhlfe); MPLS_EXIT; }
int mpls_set_out_label_mtu(struct mpls_out_label_req *out) { struct mpls_nhlfe *nhlfe = NULL; int retval = 0; unsigned int key; BUG_ON(!out); MPLS_ENTER; key = out->mol_label.u.ml_key; nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u does not exists in radix tree\n", key); MPLS_EXIT; return -ESRCH; } /* Update the MTU if possible */ if (nhlfe->nhlfe_mtu_limit >= out->mol_mtu) { nhlfe->nhlfe_mtu = out->mol_mtu; } else { MPLS_DEBUG("MTU is larger than lower layer (%d > %d)\n", out->mol_mtu, nhlfe->nhlfe_mtu_limit); /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release(nhlfe); return -EINVAL; } /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release(nhlfe); /* force the layer 3 protocols to re-find and dsts (NHLFEs), * thus picking up the new MTU */ mpls_proto_cache_flush_all(&init_net); MPLS_EXIT; return retval; }
int mpls_set_out_label_propagate_ttl(struct mpls_out_label_req *mol) { unsigned int key = mpls_label2key(0,&mol->mol_label); struct mpls_nhlfe *nhlfe = mpls_get_nhlfe(key); if (!nhlfe) return -ESRCH; nhlfe->nhlfe_propagate_ttl = mol->mol_propagate_ttl; mpls_nhlfe_release(nhlfe); return 0; }
int mpls_set_out_label_instrs (struct mpls_instr_req *mir) { struct mpls_label *ml = &mir->mir_label; unsigned int key = mpls_label2key(0,ml); struct mpls_nhlfe *nhlfe = mpls_get_nhlfe(key); int ret; if (unlikely(!nhlfe)) return -ESRCH; ret = mpls_set_out_instrs (mir->mir_instr,mir->mir_instr_length, nhlfe); mpls_nhlfe_release(nhlfe); return ret; }
struct mpls_nhlfe* mpls_remove_nhlfe (unsigned int key) { struct mpls_nhlfe *nhlfe = NULL; MPLS_ENTER; nhlfe = radix_tree_delete(&mpls_nhlfe_tree, key); if (!nhlfe) MPLS_DEBUG("NHLFE node with key %u not found.\n",key); list_del_rcu(&nhlfe->global); /* release the refcnt for the tree hold it */ mpls_nhlfe_release (nhlfe); MPLS_EXIT; return nhlfe; }
int mpls_set_nexthop (struct shim_blk *sblk, struct dst_entry *dst) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key; int ret; MPLS_ENTER; memcpy(&key, sblk->data, sizeof(key)); nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_EXIT; return -ENXIO; } ret = mpls_set_nexthop2(nhlfe, dst); mpls_nhlfe_release(nhlfe); MPLS_EXIT; return ret; }
int mpls_del_out_label(struct mpls_out_label_req *out) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key; MPLS_ENTER; key = mpls_label2key(0,&out->mol_label); nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u was not in tree\n",key); MPLS_EXIT; return -ESRCH; } spin_lock_bh (&mpls_nhlfe_lock); /* at this point a NHLFE that can be deleted will have a refcnt * of 2, one from mpls_get_nhlfe() we just executed and the * other that from when it was added to the tree */ if (atomic_read(&nhlfe->__refcnt) > 2) { /* someone else is hold a refcnt, we can't delete */ /* release the refcnt we aquired in mpls_get_nhlfe() */ mpls_nhlfe_release (nhlfe); spin_unlock_bh (&mpls_nhlfe_lock); MPLS_DEBUG("Node %u is being used\n",key); MPLS_EXIT; return -EBUSY; } /* * This code starts the process of removing a NHLFE from the * system. The first thing we we do it remove it from the tree * so no one else can get a reference to it. Then we notify the * higher layer protocols that they should give up thier references * soon (does not need to happen immediatly, the dst system allows * for this. Finally we schedule the RCU system to call * dst_rcu_free() which waits until all CPUs have finished * thier current work and then calls dst_rcu_free() which * kicks the dst system into action once the dst system knows * everyone is done using this "dst" it calls mpls_dst_destroy(). */ /* remove the NHLFE from the tree (which decs the refcnt we held when * it was added to the tree) */ mpls_remove_nhlfe(nhlfe->nhlfe_key); spin_unlock_bh (&mpls_nhlfe_lock); mpls_nhlfe_event(MPLS_CMD_DELNHLFE, nhlfe, 0, 0); /* destrory the instructions on this nhlfe, so as to no longer * hold refs to interfaces and other NHLFEs. * * Remember NHLFEs may stick around in the dst system even * after we've removed it from the tree. So this will result * in traffic using the NHLFE to be dropped */ mpls_destroy_out_instrs (nhlfe); /* let the dst system know we're done with this NHLFE and * schedule all higher layer protocol to give up their references */ dst_release(&nhlfe->u.dst); nhlfe->u.dst.obsolete = 1; mpls_proto_cache_flush_all(&init_net); /* since high layer protocols may still be using us in there caches * we need to use call_rcu() and dst_rcu_free() to take care * of actually cleaning up NHLFE */ call_rcu(&nhlfe->u.dst.rcu_head, dst_rcu_free); /* release the refcnt we aquired in mpls_get_nhlfe() */ mpls_nhlfe_release (nhlfe); MPLS_EXIT; return 0; }
int mpls_add_out_label (struct mpls_out_label_req *out, int seq, int pid) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key = 0; int retval = 0; MPLS_ENTER; BUG_ON(!out); /* Create a new key */ key = mpls_get_out_key(); /* * Check if the NHLFE is already in the tree. * It should not exist. In fact, it is impossible :) */ nhlfe = mpls_get_nhlfe (key); if (unlikely(nhlfe)) { MPLS_DEBUG("Node %u already exists in radix tree\n",key); /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release (nhlfe); retval = -EEXIST; goto error; } /* * Allocate a new Output Information/Label, */ nhlfe = nhlfe_dst_alloc (key); if (unlikely(!nhlfe)) { retval = -ENOMEM; goto error; } /* Insert into NHLFE tree */ spin_lock_bh (&mpls_nhlfe_lock); if (unlikely(mpls_insert_nhlfe (key,nhlfe))) { spin_unlock_bh (&mpls_nhlfe_lock); nhlfe->u.dst.obsolete = 1; dst_free (&nhlfe->u.dst); goto error; } /* make sure that the dst system doesn't delete this until we're * done with it */ dst_hold(&nhlfe->u.dst); mpls_nhlfe_hold(nhlfe); spin_unlock_bh (&mpls_nhlfe_lock); /* we need to hold a ref to the nhlfe while calling * mpls_nhlfe_event so it can't disappear */ mpls_nhlfe_event(MPLS_CMD_NEWNHLFE, nhlfe, seq, pid); mpls_nhlfe_release(nhlfe); out->mol_label.ml_type = MPLS_LABEL_KEY; out->mol_label.u.ml_key = key; error: MPLS_EXIT; return retval; }
int mpls_detach_in2out(struct mpls_xconnect_req *req) { struct mpls_instr *mi = NULL; struct mpls_nhlfe *nhlfe = NULL; struct mpls_ilm *ilm = NULL; unsigned int key = 0; int labelspace; int ret = 0; MPLS_ENTER; BUG_ON(!req); /* Hold a ref to the ILM, The 'in' segment */ labelspace = req->mx_in.ml_index; key = mpls_label2key(labelspace,&(req->mx_in)); ilm = mpls_get_ilm(key); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); ret = -ESRCH; goto err_no_ilm; } /* Check that there is an instruction set! */ if (unlikely(!ilm->ilm_instr)) { MPLS_DEBUG("No instruction Set!") ret = -ESRCH; goto err_no_ilm_instr; } /* Fetch the last instr, make sure it is FWD*/ for (mi = ilm->ilm_instr; mi->mi_next;mi = mi->mi_next); /* nop*/ if (!mi || mi->mi_opcode != MPLS_OP_FWD) { MPLS_DEBUG("opcode not found!\n"); ret = -ENXIO; goto err_no_fwd; } /* Get the current held nhlfe for the last in instr */ nhlfe = mi->mi_data; key = mpls_label2key(0,&(req->mx_out)); /* Make sure it is the good nhlfe */ if (!nhlfe || key != nhlfe->nhlfe_key) { /* Do not release the NHLFE, it was invalid */ MPLS_DEBUG("Invalid NHLFE %u\n",key); ret = -ENXIO; goto err_no_nhlfe; } /* The new last opcode for this ILM is now peek */ mi->mi_opcode = MPLS_OP_PEEK; /* With no data */ mi->mi_data = NULL; /* Release the NHLFE held by the Opcode (cf. mpls_attach_in2out) */ mpls_xc_event(MPLS_CMD_DELXC, ilm, nhlfe); mpls_nhlfe_release(nhlfe); ret = 0; err_no_nhlfe: err_no_fwd: /* Release the ILM after use */ mpls_ilm_release(ilm); err_no_ilm_instr: err_no_ilm: MPLS_EXIT; return ret; }
int mpls_attach_in2out(struct mpls_xconnect_req *req) { struct mpls_instr *mi = NULL; struct mpls_nhlfe *nhlfe = NULL; struct mpls_ilm *ilm = NULL; unsigned short op = 0; int labelspace, key; MPLS_ENTER; labelspace = req->mx_in.ml_index; /* Hold a ref to the ILM */ key = mpls_label2key(labelspace,&(req->mx_in)); ilm = mpls_get_ilm(key); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); MPLS_EXIT; return -ESRCH; } /* Hold a ref to the NHLFE */ key = mpls_label2key(0,&(req->mx_out)); nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); mpls_ilm_release(ilm); MPLS_EXIT; return -ESRCH; } if (unlikely(!ilm->ilm_instr)) { MPLS_DEBUG("No instruction Set!") mpls_ilm_release(ilm); mpls_nhlfe_release(nhlfe); MPLS_EXIT; return -ESRCH; } /* * Update the instructions: now, instead of "DLV"/"PEEK", now * we "FWD". The NHLFE is not released (is held by the opcode). */ /* Lookup the last instr */ for (mi = ilm->ilm_instr; mi->mi_next;mi = mi->mi_next); /* nop*/ op = mi->mi_opcode; switch (op) { case MPLS_OP_DLV: mi->mi_opcode = MPLS_OP_FWD; mi->mi_data = (void*)nhlfe; break; case MPLS_OP_FWD: mpls_xc_event(MPLS_CMD_DELXC, ilm, _mpls_as_nhlfe(mi->mi_data)); mpls_nhlfe_release(_mpls_as_nhlfe(mi->mi_data)); mi->mi_data = (void*)nhlfe; break; case MPLS_OP_PEEK: mi->mi_opcode = MPLS_OP_FWD; mi->mi_data = (void*)nhlfe; break; } mpls_xc_event(MPLS_CMD_NEWXC, ilm, nhlfe); mpls_ilm_release(ilm); return 0; }
static inline void mpls_nhlfe_release_safe (struct mpls_nhlfe *nhlfe) { if (nhlfe) mpls_nhlfe_release (nhlfe); }