Ejemplo n.º 1
0
static inline void frag_push(struct sfm_frag** head, struct sfm_frag* frag)
{
	register struct sfm_frag* old;
	register struct sfm_frag* crt;
	
	crt=(void*)atomic_get_long(head);
	do{
		frag->u.nxt_free=crt;
		old=crt;
		membar_write_atomic_op();
		crt=(void*)atomic_cmpxchg_long((void*)head, (long)old, (long)frag);
	}while(crt!=old);
}
Ejemplo n.º 2
0
static inline struct sfm_frag* frag_pop(struct sfm_frag** head)
{
	register struct sfm_frag* old;
	register struct sfm_frag* crt;
	register struct sfm_frag* nxt;
	
	crt=(void*)atomic_get_long(head);
	do{
		/* if circular list, test not needed */
		nxt=crt?crt->u.nxt_free:0;
		old=crt;
		membar_read_atomic_op();
		crt=(void*)atomic_cmpxchg_long((void*)head, (long)old, (long)nxt);
	}while(crt!=old);
	return crt;
}
Ejemplo n.º 3
0
/* lockless insert: should be always safe */
int insert_tmcb(struct tmcb_head_list *cb_list, int types,
				transaction_cb f, void *param,
				release_tmcb_param rel_func)
{
	struct tm_callback *cbp;
	struct tm_callback *old;


	/* build a new callback structure */
	if (!(cbp=shm_malloc( sizeof( struct tm_callback)))) {
		LOG(L_ERR, "ERROR:tm:insert_tmcb: out of shm. mem\n");
		return E_OUT_OF_MEM;
	}

	atomic_or_int(&cb_list->reg_types, types);
	/* ... and fill it up */
	cbp->callback = f;
	cbp->param = param;
	cbp->release = rel_func;
	cbp->types = types;
	cbp->id=0;
	old=(struct tm_callback*)cb_list->first;
	/* link it into the proper place... */
	do{
		cbp->next = old;
		/*
		if (cbp->next)
			cbp->id = cbp->next->id+1;
		else
			cbp->id = 0;
		 -- callback ids are useless -- andrei */
		membar_write_atomic_op();
		old=(void*)atomic_cmpxchg_long((void*)&cb_list->first,
										(long)old, (long)cbp);
	}while(old!=cbp->next);

	return 1;
}
Ejemplo n.º 4
0
/* should be called directly only if one of the condition bellow is true:
 *  - prepare_cancel_branch or prepare_to_cancel returned true for this branch
 *  - buffer value was 0 and then set to BUSY in an atomic op.:
 *     if (atomic_cmpxchg_long(&buffer, 0, BUSY_BUFFER)==0).
 *
 * params:  t - transaction
 *          branch - branch number to be canceled
 *          reason - cancel reason structure
 *          flags - howto cancel: 
 *                   F_CANCEL_B_KILL - will completely stop the 
 *                     branch (stops the timers), use with care
 *                   F_CANCEL_B_FAKE_REPLY - will send a fake 487
 *                      to all branches that haven't received any response
 *                      (>=100). It assumes the REPLY_LOCK is not held
 *                      (if it is => deadlock)
 *                  F_CANCEL_B_FORCE_C - will send a cancel (and create the 
 *                       corresp. local cancel rb) even if no reply was 
 *                       received; F_CANCEL_B_FAKE_REPLY will be ignored.
 *                  F_CANCEL_B_FORCE_RETR - don't stop retransmission if no 
 *                       reply was received on the branch; incompatible
 *                       with F_CANCEL_B_FAKE_REPLY, F_CANCEL_B_FORCE_C and
 *                       F_CANCEL_B_KILL (all of them take precedence) a
 *                  default: stop only the retransmissions for the branch
 *                      and leave it to timeout if it doesn't receive any
 *                      response to the CANCEL
 * returns: 0 - branch inactive after running cancel_branch() 
 *          1 - branch still active  (fr_timer)
 *         -1 - error
 * WARNING:
 *          - F_CANCEL_B_KILL should be used only if the transaction is killed
 *            explicitly afterwards (since it might kill all the timers
 *            the transaction won't be able to "kill" itself => if not
 *            explicitly "put_on_wait" it might live forever)
 *          - F_CANCEL_B_FAKE_REPLY must be used only if the REPLY_LOCK is not
 *            held
 *          - checking for buffer==0 under REPLY_LOCK is no enough, an 
 *           atomic_cmpxhcg or atomic_get_and_set _must_ be used.
 */
int cancel_branch( struct cell *t, int branch,
	#ifdef CANCEL_REASON_SUPPORT
					struct cancel_reason* reason,
	#endif /* CANCEL_REASON_SUPPORT */
					int flags )
{
	char *cancel;
	unsigned int len;
	struct retr_buf *crb, *irb;
	int ret;
	struct cancel_info tmp_cd;
	void* pcbuf;

	crb=&t->uac[branch].local_cancel;
	irb=&t->uac[branch].request;
	irb->flags|=F_RB_CANCELED;
	ret=1;
	init_cancel_info(&tmp_cd);

#	ifdef EXTRA_DEBUG
	if (crb->buffer!=BUSY_BUFFER) {
		LOG(L_CRIT, "ERROR: attempt to rewrite cancel buffer: %p\n",
				crb->buffer);
		abort();
	}
#	endif

	if (flags & F_CANCEL_B_KILL){
		stop_rb_timers( irb );
		ret=0;
		if ((t->uac[branch].last_received < 100) &&
				!(flags & F_CANCEL_B_FORCE_C)) {
			DBG("DEBUG: cancel_branch: no response ever received: "
			    "giving up on cancel\n");
			/* remove BUSY_BUFFER -- mark cancel buffer as not used */
			pcbuf=&crb->buffer; /* workaround for type punning warnings */
			atomic_set_long(pcbuf, 0);
			/* try to relay auto-generated 487 canceling response only when
			 * another one is not under relaying on the branch and there is
			 * no forced response per transaction from script */
			if((flags & F_CANCEL_B_FAKE_REPLY)
					&& !(irb->flags&F_RB_RELAYREPLY)
					&& !(t->flags&T_ADMIN_REPLY)) {
				LOCK_REPLIES(t);
				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == 
										RPS_ERROR){
					return -1;
				}
			}
			/* do nothing, hope that the caller will clean up */
			return ret;
		}
	}else{
		if (t->uac[branch].last_received < 100){
			if (!(flags & F_CANCEL_B_FORCE_C)) {
				/* no response received => don't send a cancel on this branch,
				 *  just drop it */
				if (!(flags & F_CANCEL_B_FORCE_RETR))
					stop_rb_retr(irb); /* stop retransmissions */
				/* remove BUSY_BUFFER -- mark cancel buffer as not used */
				pcbuf=&crb->buffer; /* workaround for type punning warnings */
				atomic_set_long(pcbuf, 0);
				if (flags & F_CANCEL_B_FAKE_REPLY){
					stop_rb_timers( irb ); /* stop even the fr timer */
					LOCK_REPLIES(t);
					if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1)== 
											RPS_ERROR){
						return -1;
					}
					return 0; /* should be inactive after the 487 */
				}
				/* do nothing, just wait for the final timeout */
				return 1;
			}
		}
		stop_rb_retr(irb); /* stop retransmissions */
	}

	if (cfg_get(tm, tm_cfg, reparse_invite) ||
			(t->uas.request && t->uas.request->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO))) {
		/* build the CANCEL from the INVITE which was sent out */
		cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN,
									 (t->uas.request && t->uas.request->msg_flags&FL_USE_UAC_TO)?0:&t->to
	#ifdef CANCEL_REASON_SUPPORT
									 , reason
	#endif /* CANCEL_REASON_SUPPORT */
									 );
	} else {
		/* build the CANCEL from the received INVITE */
		cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to
	#ifdef CANCEL_REASON_SUPPORT
								, reason
	#endif /* CANCEL_REASON_SUPPORT */
								);
	}
	if (!cancel) {
		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
		/* remove BUSY_BUFFER -- mark cancel buffer as not used */
		pcbuf=&crb->buffer; /* workaround for type punning warnings */
		atomic_set_long(pcbuf, 0);
		return -1;
	}
	/* install cancel now */
	crb->dst = irb->dst;
	crb->branch = branch;
	/* label it as cancel so that FR timer can better know how to
	   deal with it */
	crb->activ_type = TYPE_LOCAL_CANCEL;
	/* be extra carefully and check for bugs (the below if could be replaced
	 *  by an atomic_set((void*)&crb->buffer, cancel) */
	if (unlikely(atomic_cmpxchg_long((void*)&crb->buffer, (long)BUSY_BUFFER,
							(long)cancel)!= (long)BUSY_BUFFER)){
		BUG("tm: cancel_branch: local_cancel buffer=%p != BUSY_BUFFER"
				" (trying to continue)\n", crb->buffer);
		shm_free(cancel);
		return -1;
	}
	membar_write_atomic_op(); /* cancel retr. can be called from 
								 reply_received w/o the reply lock held => 
								 they check for buffer_len to 
								 see if a valid reply exists */
	crb->buffer_len = len;

	DBG("DEBUG: cancel_branch: sending cancel...\n");
	if (SEND_BUFFER( crb )>=0){
		if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_OUT)))
			run_trans_callbacks_with_buf(TMCB_REQUEST_OUT, crb, t->uas.request, 0, TMCB_LOCAL_F);
		if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_SENT)))
			run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, crb, t->uas.request, 0, TMCB_LOCAL_F);
	}
	/*sets and starts the FINAL RESPONSE timer */
	if (start_retr( crb )!=0)
		LOG(L_CRIT, "BUG: cancel_branch: failed to start retransmission"
					" for %p\n", crb);
	return ret;
}
Ejemplo n.º 5
0
/**
 * @return: 
 * 	0: success
 * 	-1: internal error
 * 	-2: insane call :)
 */
int ack_local_uac(struct cell *trans, str *hdrs, str *body)
{
	struct retr_buf *local_ack, *old_lack;
	int ret;
	struct tmcb_params onsend_params;

	/* sanity checks */

#ifdef EXTRA_DEBUG
	if (! trans) {
		BUG("no transaction to ACK.\n");
		abort();
	}
#endif

#define RET_INVALID \
		ret = -2; \
		goto fin

	if (! is_local(trans)) {
		ERR("trying to ACK non local transaction (T@%p).\n", trans);
		RET_INVALID;
	}
	if (! is_invite(trans)) {
		ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans);
		RET_INVALID;
	}
	if (! trans->uac[0].reply) {
		ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans);
		RET_INVALID;
	}

	if (! (trans->flags & T_NO_AUTO_ACK)) {
		ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans);
		RET_INVALID;
	}
	if (trans->uac[0].local_ack) {
		ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans);
		RET_INVALID;
	}

	/* looks sane: build the retransmission buffer */

	if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, 
			hdrs, body))) {
		ERR("failed to build ACK retransmission buffer");
		RET_INVALID;
	} else {
		/* set the new buffer, but only if not already set (conc. invok.) */
		if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
				(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
			/* buffer already set: deny current attempt */
			ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans);
			free_local_ack(local_ack);
			RET_INVALID;
		}
	}

	if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){
		/* hopefully will succeed on next 2xx retransmission */
		ERR("failed to send local ACK (T@%p).\n", trans);
		ret = -1;
		goto fin;
	}
	else {
		INIT_TMCB_ONSEND_PARAMS(onsend_params, 0, 0, &trans->uac[0].request,
								&local_ack->dst,
								local_ack->buffer, local_ack->buffer_len,
								TMCB_LOCAL_F, 0 /* branch */, TYPE_LOCAL_ACK);
		run_trans_callbacks_off_params(TMCB_REQUEST_SENT, trans, &onsend_params);
	}

	ret = 0;
fin:
	/* TODO: ugly! */
	/* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is
	 * ref-counted. The t_unref() can not be used, as it requests a valid SIP
	 * message (all available might be the reply, but if AS goes wrong and
	 * tries to ACK before the final reply is received, we still have to
	 * lookup the T to find this out). */
	UNREF( trans );
	return ret;

#undef RET_INVALID
}