📄 mpid_win_lock.c
字号:
}/** * \brief Progress (advance) wait for window lock to be released * * Adds a dummy waiter to the lock wait queue, so ensure that * unlock will eventually give us a chance. * * Called from various epoch-start code to ensure no other node is * accessing our window while we are in another epoch. * * \todo Probably sohuld assert that the popped waiter, * if any, was our NULL one. * * \param[in] win Pointer to MPID_Win object * \return nothing */void MPIDU_Spin_lock_free(MPID_Win *win) { MPIDU_add_waiter(win, 0, 0, NULL); MPIDU_Progress_spin(!MPID_LOCK_IS_FREE(win)); MPIDU_pop_waiter(win, NULL, NULL, NULL);}int MPIDU_is_lock_free(MPID_Win *win) { return MPID_LOCK_IS_FREE(win);}/* * * * * * Unlock wait queue * * * * * *//** * \page unlk_wait_design Unlock Wait Queue Design * * The Unlock Wait Queue is used to delay unlocking of a * window until all outstanding RMA operations have completed. * * Each unlock request includes the number of RMA operations * that were initiated by that origin. When the unlock is attempted, * this number is compared to the count of RMA ops received from that * origin and if the numbers do not match the unlock request is queued. * * Whenever an RMA operation is processed, the routine \e rma_recvs_cb() * is called and the unlock wait queue is checked for an entry from that * origin node, and that the counts now match. If so, the unlock is dequeued * and the lock released (acknowledge message sent to origin). * * Note that this unlock may include granting of the lock to other * nodes (i.e. processing of the lock wait queue). * * \ref lock_wait_design *//** * \brief Shortcut for accessing the lock waiter queue * object in the window structure. */#define MPIDU_WIN_UNLK_QUEUE(w) ((struct mpid_qhead *)(w)->_dev._unlk_queue)/** \brief Number of Unlock Wait Queue elements per allocation block */#define MPIDU_NUM_UNLK_ENTRIES 7/** * \brief Unlock Wait Queue Element */struct mpid_unlk_entry { struct mpid_unlk_entry *next; /**< next used or next free */ int rank; /**< origin rank (unlocker) */ int rmas; /**< number of rmas sent by origin */ int _pad; /**< pad to power of 2 size */};/** \brief Padding for Datatype Cache Element resource block header */#define MPIDU_PAD_UNLK_ENTRIES 0void mpidu_init_lock(MPID_Win *win) { MPIDU_INIT_QHEAD(MPIDU_WIN_LOCK_QUEUE(win), MPIDU_NUM_ALLOC_WAITERS, sizeof(struct mpid_lock_waiter), MPIDU_PAD_ALLOC_WAITERS); MPIDU_INIT_QHEAD(MPIDU_WIN_UNLK_QUEUE(win), MPIDU_NUM_UNLK_ENTRIES, sizeof(struct mpid_unlk_entry), MPIDU_PAD_UNLK_ENTRIES);}void mpidu_free_lock(MPID_Win *win) { MPID_assert_debug(MPIDU_WIN_LOCK_QUEUE(win)->blocks == NULL || MPIDU_WIN_LOCK_QUEUE(win)->blocks->next_used == NULL); MPIDU_free_resource(MPIDU_WIN_LOCK_QUEUE(win)); MPIDU_free_resource(MPIDU_WIN_UNLK_QUEUE(win));}/** * \brief Callback function to match unlock wait queue entry * * 'v1' is a struct mpid_unlk_entry with rank and rmas filled in with * desired origin rank and current RMA ops count from origin. * 'v2' is the (currrent) struct mpid_unlk_entry being examined as * a potential match. * * Returns success (match) if an unlock element exists with origin rank * and the expected RMA ops count from that rank has been reached. * * \param[in] v1 Desired unlock queue pseudo-element * \param[in] v2 Unlock queue element to compare with 'v1' * \param[in] v3 not used * \return boolean indicating if 'v2' matches 'v1'. * * \ref unlk_wait_design */static int mpid_match_unlk(void *v1, void *v2, void *v3) { struct mpid_unlk_entry *w1 = (struct mpid_unlk_entry *)v1; struct mpid_unlk_entry *w2 = (struct mpid_unlk_entry *)v2; return (w1->rank != w2->rank || w1->rmas < w2->rmas);}/** * \brief Locate the desired unlocker if it is waiting * * Does not return success unless the RMA ops counters match. * * \param[in] win Pointer to window * \param[in] rank Origin rank (unlocker) * \param[out] ctl Reconstructed UNLOCK message, if unlocker found * * \ref unlk_wait_design * \ref msginfo_usage */static struct mpid_unlk_entry *MPIDU_locate_unlk(MPID_Win *win, int rank, MPIDU_Onesided_ctl_t *ctl) { struct mpid_unlk_entry el, *ep; struct mpid_element *pp = NULL; el.rank = rank; el.rmas = win->_dev.coll_info[rank].rma_sends; ep = MPIDU_find_element(MPIDU_WIN_UNLK_QUEUE(win), mpid_match_unlk, NULL, &el, &pp); if (ep) { if (ctl) { ctl->mpid_ctl_w0 = MPID_MSGTYPE_UNLOCK; ctl->mpid_ctl_w1 = win->handle; ctl->mpid_ctl_w2 = ep->rank; ctl->mpid_ctl_w3 = ep->rmas; } MPIDU_free_element(MPIDU_WIN_UNLK_QUEUE(win), ep, pp); } return ep;}/** * \brief Add an (unsuccessful) unlocker to the wait queue * * Decomposes the UNLOCK message to save needed data. * * \param[in] win Pointer to window * \param[in] rank Origin rank (unlocker) * \param[in] ctl UNLOCK message * * \ref unlk_wait_design * \ref msginfo_usage */static void MPIDU_add_unlk(MPID_Win *win, int rank, const MPIDU_Onesided_ctl_t *ctl) { struct mpid_unlk_entry wp; MPID_assert_debug(ctl != NULL); MPID_assert_debug(rank == ctl->mpid_ctl_w2); wp.rank = rank; wp.rmas = ctl->mpid_ctl_w3; (void)MPIDU_add_element(MPIDU_WIN_UNLK_QUEUE(win), &wp);}/** * \brief Callback invoked to count an RMA operation received * * Increments window's \e my_rma_recvs counter. * If window lock is held, then also increment RMA counter * for specific origin node, and check whether this RMA op * completes the epoch and an unlock is waiting to be processed. * * We use \e rma_sends to count received RMA ops because we * know we won't be using that to count sent RMA ops since * we cannot be in an access epoch while in a LOCK exposure epoch. * * Called from both the "long message" completion callbacks and * the "short message" receive callback, in case of PUT or * ACCUMULATE only. * * \param[in] win Pointer to MPID_Win object * \param[in] orig Rank of originator of RMA operation * \param[in] lpid lpid of originator of RMA operation * \return nothing */void rma_recvs_cb(MPID_Win *win, int orig, int lpid) { ++win->_dev.my_rma_recvs; if (!MPID_LOCK_IS_FREE(win)) { struct mpid_unlk_entry *ep; MPIDU_Onesided_ctl_t ctl; ++win->_dev.coll_info[orig].rma_sends; ep = MPIDU_locate_unlk(win, orig, &ctl); if (ep) { unlk_cb(&ctl, lpid); } }}/** * \brief Lock receive callback. * * Attempts to acquire the lock. * On success, sends ACK to origin. * On failure to acquire lock, * adds caller to lock wait queue. * * Does not attempt to acquire lock (counted as failure) * if window is currently in some other epoch. * * \param[in] info Pointer to msginfo from origin (locker) * \param[in] lpid lpid of origin node (locker) * \return nothing * * \ref msginfo_usage\n * \ref lock_design */void lock_cb(const MPIDU_Onesided_ctl_t *info, int lpid){ MPID_Win *win; int ret; int orig, type; MPIDU_Onesided_ctl_t ack; MPID_assert_debug(info->mpid_ctl_w0 == MPID_MSGTYPE_LOCK); MPID_Win_get_ptr((MPI_Win)info->mpid_ctl_w1, win); MPID_assert_debug(win != NULL); orig = info->mpid_ctl_w2; type = info->mpid_ctl_w3; ack.mpid_ctl_w0 = MPID_MSGTYPE_LOCKACK; ack.mpid_ctl_w1 = win->_dev.coll_info[orig].win_handle; ack.mpid_ctl_w2 = lpid; ack.mpid_ctl_w3 = 0; ret = (win->_dev.epoch_type == MPID_EPOTYPE_NONE && local_lock(win, orig, type)); if (!ret) { MPIDU_add_waiter(win, orig, type, &ack); } else { win->_dev.epoch_rma_ok = 1; (void) DCMF_Control(&bg1s_ct_proto, win->_dev.my_cstcy, lpid, &ack.ctl); }}/** * \brief Epoch End callback. * * Called whenever epoch_type is set to MPID_EPOTYPE_NONE, i.e. an * access/exposure epoch ends. Also called when the window lock is * released (by the origin node). * * This is used to prevent locks from being acquired while some other * access/exposure epoch is active on a window, and queues the lock * attempt until such time as the epoch has ended. * * \param[in] win Pointer to MPID_Win whose epoch has ended */void epoch_end_cb(MPID_Win *win) { int rank, type, lpid; MPIDU_Onesided_ctl_t info; int ret; /* * Wake up any waiting lockers. * * This works in the case of a shared-lock release when not all * lockers have released (no compatible waiter will be found). * * This also works in the case of non-lock epochs ending. * * An epoch-start call will spin waiting for lock to be released. * Before spinning, it will queue a waiter with lock type 0 (none), * so that this loop will not block progress indefinitely. */ while (MPIDU_pop_waiter(win, &rank, &type, &info) == 0 && type != 0) { /* compatible waiter found */ ret = local_lock(win, rank, type); MPID_assert_debug(ret != 0); if (info.mpid_ctl_w0 == 0) { /* local request */ ++win->_dev.my_sync_done; } else { win->_dev.epoch_rma_ok = 1; lpid = info.mpid_ctl_w2; (void) DCMF_Control(&bg1s_ct_proto, win->_dev.my_cstcy, lpid, &info.ctl); } }}/** * \brief Unlock receive callback. * * Attempts to release the lock. * If the lock cannot be released (due to outstanding RMA ops not * yet received) then the unlocker is placed on a queue where its * request will be re-evaluated when RMA ops are received. * If lock can be released, any lock waiters are woken up in * \e epoch_end_cb() and an MPID_MSGTYPE_UNLOCKACK is sent to the unlocker. * * \param[in] info Pointer to msginfo from origin (unlocker) * \param[in] lpid lpid of origin node (unlocker) * \return nothing
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -