/* return head of link group list and its lock for a given link group */ staticinlinestruct list_head *smc_lgr_list_head(struct smc_link_group *lgr,
spinlock_t **lgr_lock)
{ if (lgr->is_smcd) {
*lgr_lock = &lgr->smcd->lgr_lock; return &lgr->smcd->lgr_list;
}
staticvoid smc_lgr_schedule_free_work(struct smc_link_group *lgr)
{ /* client link group creation always follows the server link group * creation. For client use a somewhat higher removal delay time, * otherwise there is a risk of out-of-sync link groups.
*/ if (!lgr->freeing) {
mod_delayed_work(system_wq, &lgr->free_work,
(!lgr->is_smcd && lgr->role == SMC_CLNT) ?
SMC_LGR_FREE_DELAY_CLNT :
SMC_LGR_FREE_DELAY_SERV);
}
}
/* Register connection's alert token in our lookup structure. * To use rbtrees we have to implement our own insert core. * Requires @conns_lock * @smc connection to register * Returns 0 on success, != otherwise.
*/ staticvoid smc_lgr_add_alert_token(struct smc_connection *conn)
{ struct rb_node **link, *parent = NULL;
u32 token = conn->alert_token_local;
link = &conn->lgr->conns_all.rb_node; while (*link) { struct smc_connection *cur = rb_entry(*link, struct smc_connection, alert_node);
parent = *link; if (cur->alert_token_local > token)
link = &parent->rb_left; else
link = &parent->rb_right;
} /* Put the new node there */
rb_link_node(&conn->alert_node, parent, link);
rb_insert_color(&conn->alert_node, &conn->lgr->conns_all);
}
/* assign an SMC-R link to the connection */ staticint smcr_lgr_conn_assign_link(struct smc_connection *conn, bool first)
{ enum smc_link_state expected = first ? SMC_LNK_ACTIVATING :
SMC_LNK_ACTIVE; int i, j;
/* do link balancing */
conn->lnk = NULL; /* reset conn->lnk first */ for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { struct smc_link *lnk = &conn->lgr->lnk[i];
if (lnk->state != expected || lnk->link_is_asym) continue; if (conn->lgr->role == SMC_CLNT) {
conn->lnk = lnk; /* temporary, SMC server assigns link*/ break;
} if (conn->lgr->conns_num % 2) { for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) { struct smc_link *lnk2;
/* Register connection in link group by assigning an alert token * registered in a search tree. * Requires @conns_lock * Note that '0' is a reserved value and not assigned.
*/ staticint smc_lgr_register_conn(struct smc_connection *conn, bool first)
{ struct smc_sock *smc = container_of(conn, struct smc_sock, conn); static atomic_t nexttoken = ATOMIC_INIT(0); int rc;
if (!conn->lgr->is_smcd) {
rc = smcr_lgr_conn_assign_link(conn, first); if (rc) {
conn->lgr = NULL; return rc;
}
} /* find a new alert_token_local value not yet used by some connection * in this link group
*/
sock_hold(&smc->sk); /* sock_put in smc_lgr_unregister_conn() */ while (!conn->alert_token_local) {
conn->alert_token_local = atomic_inc_return(&nexttoken); if (smc_lgr_find_conn(conn->alert_token_local, conn->lgr))
conn->alert_token_local = 0;
}
smc_lgr_add_alert_token(conn);
conn->lgr->conns_num++; return 0;
}
/* Unregister connection and reset the alert token of the given connection<
*/ staticvoid __smc_lgr_unregister_conn(struct smc_connection *conn)
{ struct smc_sock *smc = container_of(conn, struct smc_sock, conn); struct smc_link_group *lgr = conn->lgr;
rb_erase(&conn->alert_node, &lgr->conns_all); if (conn->lnk)
atomic_dec(&conn->lnk->conn_cnt);
lgr->conns_num--;
conn->alert_token_local = 0;
sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
}
nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&smc_gen_nl_family, NLM_F_MULTI,
SMC_NETLINK_GET_LGR_SMCR); if (!nlh) goto errmsg; if (smc_nl_fill_lgr(lgr, skb, cb)) goto errout;
genlmsg_end(skb, nlh); if (!list_links) goto out; for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { if (!smc_link_usable(&lgr->lnk[i])) continue; if (smc_nl_fill_lgr_link(lgr, &lgr->lnk[i], skb, cb)) goto errout;
}
out: return 0;
smc_lgr_list_head(lgr, &lgr_lock);
spin_lock_bh(lgr_lock); /* do not use this link group for new connections */ if (!list_empty(&lgr->list))
list_del_init(&lgr->list);
spin_unlock_bh(lgr_lock);
__smc_lgr_terminate(lgr, true);
}
staticvoid smcr_lgr_link_deactivate_all(struct smc_link_group *lgr)
{ int i;
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { struct smc_link *lnk = &lgr->lnk[i];
if (smc_link_sendable(lnk))
lnk->state = SMC_LNK_INACTIVE;
}
wake_up_all(&lgr->llc_msg_waiter);
wake_up_all(&lgr->llc_flow_waiter);
}
smc_lgr_list_head(lgr, &lgr_lock);
spin_lock_bh(lgr_lock); if (lgr->freeing) {
spin_unlock_bh(lgr_lock); return;
}
read_lock_bh(&lgr->conns_lock);
conns = RB_EMPTY_ROOT(&lgr->conns_all);
read_unlock_bh(&lgr->conns_lock); if (!conns) { /* number of lgr connections is no longer zero */
spin_unlock_bh(lgr_lock); return;
}
list_del_init(&lgr->list); /* remove from smc_lgr_list */
lgr->freeing = 1; /* this instance does the freeing, no new schedule */
spin_unlock_bh(lgr_lock);
cancel_delayed_work(&lgr->free_work);
if (!lgr->is_smcd && !lgr->terminating)
smc_llc_send_link_delete_all(lgr, true,
SMC_LLC_DEL_PROG_INIT_TERM); if (lgr->is_smcd && !lgr->terminating)
smc_ism_signal_shutdown(lgr); if (!lgr->is_smcd)
smcr_lgr_link_deactivate_all(lgr);
smc_lgr_free(lgr);
}
staticint smc_write_space(struct smc_connection *conn)
{ int buffer_len = conn->peer_rmbe_size; union smc_host_cursor prod; union smc_host_cursor cons; int space;
staticint smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend, struct smc_wr_buf *wr_buf)
{ struct smc_connection *conn = &smc->conn; union smc_host_cursor cons, fin; int rc = 0; int diff;
smc_curs_copy(&conn->tx_curs_sent, &conn->tx_curs_fin, conn);
smc_curs_copy(&fin, &conn->local_tx_ctrl_fin, conn); /* set prod cursor to old state, enforce tx_rdma_writes() */
smc_curs_copy(&conn->local_tx_ctrl.prod, &fin, conn);
smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
if (smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) { /* cons cursor advanced more than fin, and prod was set * fin above, so now prod is smaller than cons. Fix that.
*/
diff = smc_curs_diff(conn->peer_rmbe_size, &fin, &cons);
smc_curs_add(conn->sndbuf_desc->len,
&conn->tx_curs_sent, diff);
smc_curs_add(conn->sndbuf_desc->len,
&conn->tx_curs_fin, diff);
smc_curs_add(conn->peer_rmbe_size,
&conn->local_tx_ctrl.prod, diff);
smc_curs_add(conn->peer_rmbe_size,
&conn->local_tx_ctrl_fin, diff);
} /* recalculate, value is used by tx_rdma_writes() */
atomic_set(&smc->conn.peer_rmbe_space, smc_write_space(conn));
/* remove a finished connection from its link group */ void smc_conn_free(struct smc_connection *conn)
{ struct smc_link_group *lgr = conn->lgr;
if (!lgr || conn->freed) /* Connection has never been registered in a * link group, or has already been freed.
*/ return;
conn->freed = 1; if (!smc_conn_lgr_valid(conn)) /* Connection has already unregistered from * link group.
*/ goto lgr_put;
if (lgr->is_smcd) { if (!list_empty(&lgr->list))
smc_ism_unset_conn(conn); if (smc_ism_support_dmb_nocopy(lgr->smcd))
smcd_buf_detach(conn);
tasklet_kill(&conn->rx_tsklet);
} else {
smc_cdc_wait_pend_tx_wr(conn); if (current_work() != &conn->abort_work)
cancel_work_sync(&conn->abort_work);
} if (!list_empty(&lgr->list)) {
smc_buf_unuse(conn, lgr); /* allow buffer reuse */
smc_lgr_unregister_conn(conn);
}
if (!lgr->conns_num)
smc_lgr_schedule_free_work(lgr);
lgr_put: if (!lgr->is_smcd)
smcr_link_put(conn->lnk); /* link_hold in smc_conn_create() */
smc_lgr_put(lgr); /* lgr_hold in smc_conn_create() */
}
/* unregister a link from a buf_desc */ staticvoid smcr_buf_unmap_link(struct smc_buf_desc *buf_desc, bool is_rmb, struct smc_link *lnk)
{ if (is_rmb || buf_desc->is_vm)
buf_desc->is_reg_mr[lnk->link_idx] = false; if (!buf_desc->is_map_ib[lnk->link_idx]) return;
/* unmap all buffers of lgr for a deleted link */ staticvoid smcr_buf_unmap_lgr(struct smc_link *lnk)
{ struct smc_link_group *lgr = lnk->lgr; struct smc_buf_desc *buf_desc, *bf; int i;
for (i = 0; i < SMC_RMBE_SIZES; i++) {
down_write(&lgr->rmbs_lock);
list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list)
smcr_buf_unmap_link(buf_desc, true, lnk);
up_write(&lgr->rmbs_lock);
/* won't be freed until no one accesses to lgr anymore */ staticvoid __smc_lgr_free(struct smc_link_group *lgr)
{
smc_lgr_free_bufs(lgr); if (lgr->is_smcd) { if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
wake_up(&lgr->smcd->lgrs_deleted);
} else {
smc_wr_free_lgr_mem(lgr); if (!atomic_dec_return(&lgr_cnt))
wake_up(&lgrs_deleted);
}
kfree(lgr);
}
/* remove a link group */ staticvoid smc_lgr_free(struct smc_link_group *lgr)
{ int i;
if (!lgr->is_smcd) {
down_write(&lgr->llc_conf_mutex); for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { if (lgr->lnk[i].state != SMC_LNK_UNUSED)
smcr_link_clear(&lgr->lnk[i], false);
}
up_write(&lgr->llc_conf_mutex);
smc_llc_lgr_clear(lgr);
}
destroy_workqueue(lgr->tx_wq); if (lgr->is_smcd) {
smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
put_device(lgr->smcd->ops->get_dev(lgr->smcd));
}
smc_lgr_put(lgr); /* theoretically last lgr_put */
}
/* terminate link group * @soft: true if link group shutdown can take its time * false if immediate link group shutdown is required
*/ staticvoid __smc_lgr_terminate(struct smc_link_group *lgr, bool soft)
{ struct smc_connection *conn; struct smc_sock *smc; struct rb_node *node;
if (lgr->terminating) return; /* lgr already terminating */ /* cancel free_work sync, will terminate when lgr->freeing is set */
cancel_delayed_work(&lgr->free_work);
lgr->terminating = 1;
/* Called when peer lgr shutdown (regularly or abnormally) is received */ void smc_smcd_terminate(struct smcd_dev *dev, struct smcd_gid *peer_gid, unsignedshort vlan)
{ struct smc_link_group *lgr, *l;
LIST_HEAD(lgr_free_list);
/* run common cleanup function and build free list */
spin_lock_bh(&dev->lgr_lock);
list_for_each_entry_safe(lgr, l, &dev->lgr_list, list) { if ((!peer_gid->gid ||
(lgr->peer_gid.gid == peer_gid->gid &&
!smc_ism_is_emulated(dev) ? 1 :
lgr->peer_gid.gid_ext == peer_gid->gid_ext)) &&
(vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) { if (peer_gid->gid) /* peer triggered termination */
lgr->peer_shutdown = 1;
list_move(&lgr->list, &lgr_free_list);
lgr->freeing = 1;
}
}
spin_unlock_bh(&dev->lgr_lock);
/* cancel the regular free workers and actually free lgrs */
list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
list_del_init(&lgr->list);
schedule_work(&lgr->terminate_work);
}
}
/* Called when an SMCD device is removed or the smc module is unloaded */ void smc_smcd_terminate_all(struct smcd_dev *smcd)
{ struct smc_link_group *lgr, *lg;
LIST_HEAD(lgr_free_list);
if (atomic_read(&smcd->lgr_cnt))
wait_event(smcd->lgrs_deleted, !atomic_read(&smcd->lgr_cnt));
}
/* Called when an SMCR device is removed or the smc module is unloaded. * If smcibdev is given, all SMCR link groups using this device are terminated. * If smcibdev is NULL, all SMCR link groups are terminated.
*/ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
{ struct smc_link_group *lgr, *lg;
LIST_HEAD(lgr_free_list); int i;
spin_lock_bh(&smc_lgr_list.lock); if (!smcibdev) {
list_splice_init(&smc_lgr_list.list, &lgr_free_list);
list_for_each_entry(lgr, &lgr_free_list, list)
lgr->freeing = 1;
} else {
list_for_each_entry_safe(lgr, lg, &smc_lgr_list.list, list) { for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { if (lgr->lnk[i].smcibdev == smcibdev)
smcr_link_down_cond_sched(&lgr->lnk[i]);
}
}
}
spin_unlock_bh(&smc_lgr_list.lock);
if (smcibdev) { if (atomic_read(&smcibdev->lnk_cnt))
wait_event(smcibdev->lnks_deleted,
!atomic_read(&smcibdev->lnk_cnt));
} else { if (atomic_read(&lgr_cnt))
wait_event(lgrs_deleted, !atomic_read(&lgr_cnt));
}
}
/* set new lgr type and clear all asymmetric link tagging */ void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type)
{ char *lgr_type = ""; int i;
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) if (smc_link_usable(&lgr->lnk[i]))
lgr->lnk[i].link_is_asym = false; if (lgr->type == new_type) return;
lgr->type = new_type;
switch (lgr->type) { case SMC_LGR_NONE:
lgr_type = "NONE"; break; case SMC_LGR_SINGLE:
lgr_type = "SINGLE"; break; case SMC_LGR_SYMMETRIC:
lgr_type = "SYMMETRIC"; break; case SMC_LGR_ASYMMETRIC_PEER:
lgr_type = "ASYMMETRIC_PEER"; break; case SMC_LGR_ASYMMETRIC_LOCAL:
lgr_type = "ASYMMETRIC_LOCAL"; break;
}
pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu state changed: " "%s, pnetid %.16s\n", SMC_LGR_ID_SIZE, &lgr->id,
lgr->net->net_cookie, lgr_type, lgr->pnet_id);
}
/* set new lgr type and tag a link as asymmetric */ void smcr_lgr_set_type_asym(struct smc_link_group *lgr, enum smc_lgr_type new_type, int asym_lnk_idx)
{
smcr_lgr_set_type(lgr, new_type);
lgr->lnk[asym_lnk_idx].link_is_asym = true;
}
if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) continue;
/* trigger local add link processing */
link = smc_llc_usable_link(lgr); if (link)
smc_llc_add_link_local(link);
}
spin_unlock_bh(&smc_lgr_list.lock);
}
/* link is down - switch connections to alternate link, * must be called under lgr->llc_conf_mutex lock
*/ staticvoid smcr_link_down(struct smc_link *lnk)
{ struct smc_link_group *lgr = lnk->lgr; struct smc_link *to_lnk; int del_link_id;
if (!lgr || lnk->state == SMC_LNK_UNUSED || list_empty(&lgr->list)) return;
to_lnk = smc_switch_conns(lgr, lnk, true); if (!to_lnk) { /* no backup link available */
smcr_link_clear(lnk, true); return;
}
smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
del_link_id = lnk->link_id;
if (lgr->role == SMC_SERV) { /* trigger local delete link processing */
smc_llc_srv_delete_link_local(to_lnk, del_link_id);
} else { if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { /* another llc task is ongoing */
up_write(&lgr->llc_conf_mutex);
wait_event_timeout(lgr->llc_flow_waiter,
(list_empty(&lgr->list) ||
lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
SMC_LLC_WAIT_TIME);
down_write(&lgr->llc_conf_mutex);
} if (!list_empty(&lgr->list)) {
smc_llc_send_delete_link(to_lnk, del_link_id,
SMC_LLC_REQ, true,
SMC_LLC_DEL_LOST_PATH);
smcr_link_clear(lnk, true);
}
wake_up(&lgr->llc_flow_waiter); /* wake up next waiter */
}
}
/* must be called under lgr->llc_conf_mutex lock */ void smcr_link_down_cond(struct smc_link *lnk)
{ if (smc_link_downing(&lnk->state)) {
trace_smcr_link_down(lnk, __builtin_return_address(0));
smcr_link_down(lnk);
}
}
/* will get the lgr->llc_conf_mutex lock */ void smcr_link_down_cond_sched(struct smc_link *lnk)
{ if (smc_link_downing(&lnk->state)) {
trace_smcr_link_down(lnk, __builtin_return_address(0));
smcr_link_hold(lnk); /* smcr_link_put in link_down_wrk */ if (!schedule_work(&lnk->link_down_wrk))
smcr_link_put(lnk);
}
}
list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) { if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
SMC_MAX_PNETID_LEN)) continue; /* lgr is not affected */ if (list_empty(&lgr->list)) continue; for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { struct smc_link *lnk = &lgr->lnk[i];
if (smc_ism_is_emulated(smcismdev) &&
lgr->peer_gid.gid_ext != peer_gid->gid_ext) returnfalse;
returntrue;
}
/* create a new SMC connection (and a new link group if necessary) */ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
{ struct smc_connection *conn = &smc->conn; struct net *net = sock_net(&smc->sk); struct list_head *lgr_list; struct smc_link_group *lgr; enum smc_lgr_role role;
spinlock_t *lgr_lock; int rc = 0;
lgr_list = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_list :
&smc_lgr_list.list;
lgr_lock = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_lock :
&smc_lgr_list.lock;
ini->first_contact_local = 1;
role = smc->listen_smc ? SMC_SERV : SMC_CLNT; if (role == SMC_CLNT && ini->first_contact_peer) /* create new link group as well */ goto create;
/* determine if an existing link group can be reused */
spin_lock_bh(lgr_lock);
list_for_each_entry(lgr, lgr_list, list) {
write_lock_bh(&lgr->conns_lock); if ((ini->is_smcd ?
smcd_lgr_match(lgr, ini->ism_dev[ini->ism_selected],
&ini->ism_peer_gid[ini->ism_selected]) :
smcr_lgr_match(lgr, ini->smcr_version,
ini->peer_systemid,
ini->peer_gid, ini->peer_mac, role,
ini->ib_clcqpn, net)) &&
!lgr->sync_err &&
(ini->smcd_version == SMC_V2 ||
lgr->vlan_id == ini->vlan_id) &&
(role == SMC_CLNT || ini->is_smcd ||
(lgr->conns_num < lgr->max_conns &&
!bitmap_full(lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX)))) { /* link group found */
ini->first_contact_local = 0;
conn->lgr = lgr;
rc = smc_lgr_register_conn(conn, false);
write_unlock_bh(&lgr->conns_lock); if (!rc && delayed_work_pending(&lgr->free_work))
cancel_delayed_work(&lgr->free_work); break;
}
write_unlock_bh(&lgr->conns_lock);
}
spin_unlock_bh(lgr_lock); if (rc) return rc;
if (role == SMC_CLNT && !ini->first_contact_peer &&
ini->first_contact_local) { /* Server reuses a link group, but Client wants to start * a new one * send out_of_sync decline, reason synchr. error
*/ return SMC_CLC_DECL_SYNCERR;
}
create: if (ini->first_contact_local) {
rc = smc_lgr_create(smc, ini); if (rc) goto out;
lgr = conn->lgr;
write_lock_bh(&lgr->conns_lock);
rc = smc_lgr_register_conn(conn, true);
write_unlock_bh(&lgr->conns_lock); if (rc) {
smc_lgr_cleanup_early(lgr); goto out;
}
}
smc_lgr_hold(conn->lgr); /* lgr_put in smc_conn_free() */ if (!conn->lgr->is_smcd)
smcr_link_hold(conn->lnk); /* link_put in smc_conn_free() */
conn->freed = 0;
conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
conn->urg_state = SMC_URG_READ;
init_waitqueue_head(&conn->cdc_pend_tx_wq);
INIT_WORK(&smc->conn.abort_work, smc_conn_abort_work); if (ini->is_smcd) {
conn->rx_off = sizeof(struct smcd_cdc_msg);
smcd_cdc_rx_init(conn); /* init tasklet for this conn */
} else {
conn->rx_off = 0;
} #ifndef KERNEL_HAS_ATOMIC64
spin_lock_init(&conn->acurs_lock); #endif
/* convert the RMB size into the compressed notation (minimum 16K, see * SMCD/R_DMBE_SIZES. * In contrast to plain ilog2, this rounds towards the next power of 2, * so the socket application gets at least its desired sndbuf / rcvbuf size.
*/ static u8 smc_compress_bufsize(int size, bool is_smcd, bool is_rmb)
{
u8 compressed;
#ifdef CONFIG_ARCH_NO_SG_CHAIN if (!is_smcd && is_rmb) /* RMBs are backed by & limited to max size of scatterlists */
compressed = min_t(u8, compressed, ilog2((SG_MAX_SINGLE_ALLOC * PAGE_SIZE) >> 14)); #endif
return compressed;
}
/* convert the RMB size from compressed notation into integer */ int smc_uncompress_bufsize(u8 compressed)
{
u32 size;
/* try to reuse a sndbuf or rmb description slot for a certain * buffer size; if not available, return NULL
*/ staticstruct smc_buf_desc *smc_buf_get_slot(struct rw_semaphore *lock, struct list_head *buf_list)
{ struct smc_buf_desc *buf_slot;
/* one of the conditions for announcing a receiver's current window size is * that it "results in a minimum increase in the window size of 10% of the * receive buffer space" [RFC7609]
*/ staticinlineint smc_rmb_wnd_update_limit(int rmbe_size)
{ return max_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
}
/* map an buf to a link */ staticint smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb, struct smc_link *lnk)
{ int rc, i, nents, offset, buf_size, size, access_flags; struct scatterlist *sg; void *buf;
if (is_rmb || buf_desc->is_vm) { /* create a new memory region for the RMB or vzalloced sndbuf */
access_flags = is_rmb ?
IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
IB_ACCESS_LOCAL_WRITE;
/* register a new buf on IB device, rmb or vzalloced sndbuf * must be called under lgr->llc_conf_mutex lock
*/ int smcr_link_reg_buf(struct smc_link *link, struct smc_buf_desc *buf_desc)
{ if (list_empty(&link->lgr->list)) return -ENOLINK; if (!buf_desc->is_reg_mr[link->link_idx]) { /* register memory region for new buf */ if (buf_desc->is_vm)
buf_desc->mr[link->link_idx]->iova =
(uintptr_t)buf_desc->cpu_addr; if (smc_wr_reg_send(link, buf_desc->mr[link->link_idx])) {
buf_desc->is_reg_err = true; return -EFAULT;
}
buf_desc->is_reg_mr[link->link_idx] = true;
} return 0;
}
/* map all used buffers of lgr for a new link */ int smcr_buf_map_lgr(struct smc_link *lnk)
{ struct smc_link_group *lgr = lnk->lgr; int i, rc = 0;
for (i = 0; i < SMC_RMBE_SIZES; i++) {
rc = _smcr_buf_map_lgr(lnk, &lgr->rmbs_lock,
&lgr->rmbs[i], true); if (rc) return rc;
rc = _smcr_buf_map_lgr(lnk, &lgr->sndbufs_lock,
&lgr->sndbufs[i], false); if (rc) return rc;
} return 0;
}
/* register all used buffers of lgr for a new link, * must be called under lgr->llc_conf_mutex lock
*/ int smcr_buf_reg_lgr(struct smc_link *lnk)
{ struct smc_link_group *lgr = lnk->lgr; struct smc_buf_desc *buf_desc, *bf; int i, rc = 0;
/* reg all RMBs for a new link */
down_write(&lgr->rmbs_lock); for (i = 0; i < SMC_RMBE_SIZES; i++) {
list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list) { if (!buf_desc->used) continue;
rc = smcr_link_reg_buf(lnk, buf_desc); if (rc) {
up_write(&lgr->rmbs_lock); return rc;
}
}
}
up_write(&lgr->rmbs_lock);
if (lgr->buf_type == SMCR_PHYS_CONT_BUFS) return rc;
/* reg all vzalloced sndbufs for a new link */
down_write(&lgr->sndbufs_lock); for (i = 0; i < SMC_RMBE_SIZES; i++) {
list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i], list) { if (!buf_desc->used || !buf_desc->is_vm) continue;
rc = smcr_link_reg_buf(lnk, buf_desc); if (rc) {
up_write(&lgr->sndbufs_lock); return rc;
}
}
}
up_write(&lgr->sndbufs_lock); return rc;
}
/* map buf_desc on all usable links, * unused buffers stay mapped as long as the link is up
*/ staticint smcr_buf_map_usable_links(struct smc_link_group *lgr, struct smc_buf_desc *buf_desc, bool is_rmb)
{ int i, rc = 0, cnt = 0;
/* protect against parallel link reconfiguration */
down_read(&lgr->llc_conf_mutex); for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { struct smc_link *lnk = &lgr->lnk[i];
if (!smc_link_usable(lnk)) continue; if (smcr_buf_map_link(buf_desc, is_rmb, lnk)) {
rc = -ENOMEM; goto out;
}
cnt++;
}
out:
up_read(&lgr->llc_conf_mutex); if (!rc && !cnt)
rc = -EINVAL; return rc;
}
staticstruct smc_buf_desc *smcd_new_buf_create(struct smc_link_group *lgr, bool is_dmb, int bufsize)
{ struct smc_buf_desc *buf_desc; int rc;
/* try to alloc a new DMB */
buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL); if (!buf_desc) return ERR_PTR(-ENOMEM); if (is_dmb) {
rc = smc_ism_register_dmb(lgr, bufsize, buf_desc); if (rc) {
kfree(buf_desc); if (rc == -ENOMEM) return ERR_PTR(-EAGAIN); if (rc == -ENOSPC) return ERR_PTR(-ENOSPC); return ERR_PTR(-EIO);
}
buf_desc->pages = virt_to_page(buf_desc->cpu_addr); /* CDC header stored in buf. So, pretend it was smaller */
buf_desc->len = bufsize - sizeof(struct smcd_cdc_msg);
} else {
buf_desc->cpu_addr = kzalloc(bufsize, GFP_KERNEL |
__GFP_NOWARN | __GFP_NORETRY |
__GFP_NOMEMALLOC); if (!buf_desc->cpu_addr) {
kfree(buf_desc); return ERR_PTR(-EAGAIN);
}
buf_desc->len = bufsize;
} return buf_desc;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.