void rdsv3_ib_send_add_credits(struct rdsv3_connection *conn, unsigned int credits) { struct rdsv3_ib_connection *ic = conn->c_transport_data; if (credits == 0) return; RDSV3_DPRINTF5("rdsv3_ib_send_add_credits", "credits (%u): current=%u%s\n", credits, IB_GET_SEND_CREDITS(atomic_get(&ic->i_credits)), test_bit(RDSV3_LL_SEND_FULL, &conn->c_flags) ? ", ll_send_full" : ""); atomic_add_32(&ic->i_credits, IB_SET_SEND_CREDITS(credits)); if (test_and_clear_bit(RDSV3_LL_SEND_FULL, &conn->c_flags)) rdsv3_queue_delayed_work(rdsv3_wq, &conn->c_send_w, 0); ASSERT(!(IB_GET_SEND_CREDITS(credits) >= 16384)); rdsv3_ib_stats_inc(s_ib_rx_credit_updates); RDSV3_DPRINTF4("rdsv3_ib_send_add_credits", "Return: conn: %p, credits: %d", conn, credits); }
/* * This is the main function for allocating credits when sending * messages. * * Conceptually, we have two counters: * - send credits: this tells us how many WRs we're allowed * to submit without overruning the reciever's queue. For * each SEND WR we post, we decrement this by one. * * - posted credits: this tells us how many WRs we recently * posted to the receive queue. This value is transferred * to the peer as a "credit update" in a RDS header field. * Every time we transmit credits to the peer, we subtract * the amount of transferred credits from this counter. * * It is essential that we avoid situations where both sides have * exhausted their send credits, and are unable to send new credits * to the peer. We achieve this by requiring that we send at least * one credit update to the peer before exhausting our credits. * When new credits arrive, we subtract one credit that is withheld * until we've posted new buffers and are ready to transmit these * credits (see rdsv3_ib_send_add_credits below). * * The RDS send code is essentially single-threaded; rdsv3_send_xmit * grabs c_send_lock to ensure exclusive access to the send ring. * However, the ACK sending code is independent and can race with * message SENDs. * * In the send path, we need to update the counters for send credits * and the counter of posted buffers atomically - when we use the * last available credit, we cannot allow another thread to race us * and grab the posted credits counter. Hence, we have to use a * spinlock to protect the credit counter, or use atomics. * * Spinlocks shared between the send and the receive path are bad, * because they create unnecessary delays. An early implementation * using a spinlock showed a 5% degradation in throughput at some * loads. * * This implementation avoids spinlocks completely, putting both * counters into a single atomic, and updating that atomic using * atomic_add (in the receive path, when receiving fresh credits), * and using atomic_cmpxchg when updating the two counters. */ int rdsv3_ib_send_grab_credits(struct rdsv3_ib_connection *ic, uint32_t wanted, uint32_t *adv_credits, int need_posted) { unsigned int avail, posted, got = 0, advertise; long oldval, newval; RDSV3_DPRINTF4("rdsv3_ib_send_grab_credits", "ic: %p, %d %d %d", ic, wanted, *adv_credits, need_posted); *adv_credits = 0; if (!ic->i_flowctl) return (wanted); try_again: advertise = 0; oldval = newval = atomic_get(&ic->i_credits); posted = IB_GET_POST_CREDITS(oldval); avail = IB_GET_SEND_CREDITS(oldval); RDSV3_DPRINTF5("rdsv3_ib_send_grab_credits", "wanted (%u): credits=%u posted=%u\n", wanted, avail, posted); /* The last credit must be used to send a credit update. */ if (avail && !posted) avail--; if (avail < wanted) { struct rdsv3_connection *conn = ic->i_cm_id->context; /* Oops, there aren't that many credits left! */ set_bit(RDSV3_LL_SEND_FULL, &conn->c_flags); got = avail; } else { /* Sometimes you get what you want, lalala. */ got = wanted; } newval -= IB_SET_SEND_CREDITS(got); /* * If need_posted is non-zero, then the caller wants * the posted regardless of whether any send credits are * available. */ if (posted && (got || need_posted)) { advertise = min(posted, RDSV3_MAX_ADV_CREDIT); newval -= IB_SET_POST_CREDITS(advertise); } /* Finally bill everything */ if (atomic_cmpxchg(&ic->i_credits, oldval, newval) != oldval) goto try_again; *adv_credits = advertise; RDSV3_DPRINTF4("rdsv3_ib_send_grab_credits", "ic: %p, %d %d %d", ic, got, *adv_credits, need_posted); return (got); }
/* * This is the main function for allocating credits when sending * messages. * * Conceptually, we have two counters: * - send credits: this tells us how many WRs we're allowed * to submit without overruning the reciever's queue. For * each SEND WR we post, we decrement this by one. * * - posted credits: this tells us how many WRs we recently * posted to the receive queue. This value is transferred * to the peer as a "credit update" in a RDS header field. * Every time we transmit credits to the peer, we subtract * the amount of transferred credits from this counter. * * It is essential that we avoid situations where both sides have * exhausted their send credits, and are unable to send new credits * to the peer. We achieve this by requiring that we send at least * one credit update to the peer before exhausting our credits. * When new credits arrive, we subtract one credit that is withheld * until we've posted new buffers and are ready to transmit these * credits (see rds_ib_send_add_credits below). * * The RDS send code is essentially single-threaded; rds_send_xmit * grabs c_send_lock to ensure exclusive access to the send ring. * However, the ACK sending code is independent and can race with * message SENDs. * * In the send path, we need to update the counters for send credits * and the counter of posted buffers atomically - when we use the * last available credit, we cannot allow another thread to race us * and grab the posted credits counter. Hence, we have to use a * spinlock to protect the credit counter, or use atomics. * * Spinlocks shared between the send and the receive path are bad, * because they create unnecessary delays. An early implementation * using a spinlock showed a 5% degradation in throughput at some * loads. * * This implementation avoids spinlocks completely, putting both * counters into a single atomic, and updating that atomic using * atomic_add (in the receive path, when receiving fresh credits), * and using atomic_cmpxchg when updating the two counters. */ int rds_ib_send_grab_credits(struct rds_ib_connection *ic, u32 wanted, u32 *adv_credits, int need_posted, int max_posted) { unsigned int avail, posted, got = 0, advertise; long oldval, newval; *adv_credits = 0; if (!ic->i_flowctl) return wanted; try_again: advertise = 0; oldval = newval = atomic_read(&ic->i_credits); posted = IB_GET_POST_CREDITS(oldval); avail = IB_GET_SEND_CREDITS(oldval); rdsdebug("rds_ib_send_grab_credits(%u): credits=%u posted=%u\n", wanted, avail, posted); /* The last credit must be used to send a credit update. */ if (avail && !posted) avail--; if (avail < wanted) { struct rds_connection *conn = ic->i_cm_id->context; /* Oops, there aren't that many credits left! */ set_bit(RDS_LL_SEND_FULL, &conn->c_flags); got = avail; } else { /* Sometimes you get what you want, lalala. */ got = wanted; } newval -= IB_SET_SEND_CREDITS(got); /* * If need_posted is non-zero, then the caller wants * the posted regardless of whether any send credits are * available. */ if (posted && (got || need_posted)) { advertise = min_t(unsigned int, posted, max_posted); newval -= IB_SET_POST_CREDITS(advertise); }