/* * Handle a gru_mesq failure. Some of these failures are software recoverable * or retryable. */ static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, void *mesg, int lines) { int substatus, ret = 0; substatus = gru_get_cb_message_queue_substatus(cb); switch (substatus) { case CBSS_NO_ERROR: STAT(mesq_send_unexpected_error); ret = MQE_UNEXPECTED_CB_ERR; break; case CBSS_LB_OVERFLOWED: STAT(mesq_send_lb_overflow); ret = MQE_CONGESTION; break; case CBSS_QLIMIT_REACHED: STAT(mesq_send_qlimit_reached); ret = send_message_queue_full(cb, mqd, mesg, lines); break; case CBSS_AMO_NACKED: STAT(mesq_send_amo_nacked); ret = MQE_CONGESTION; break; case CBSS_PUT_NACKED: STAT(mesq_send_put_nacked); ret = send_message_put_nacked(cb, mqd, mesg, lines); break; case CBSS_PAGE_OVERFLOW: STAT(mesq_page_overflow); /* fallthru */ default: BUG(); } return ret; }
/* * Send a NOOP message to a message queue * Returns: * 0 - if queue is full after the send. This is the normal case * but various races can change this. * -1 - if mesq sent successfully but queue not full * >0 - unexpected error. MQE_xxx returned */ static int send_noop_message(void *cb, unsigned long mq, void *mesg) { const struct message_header noop_header = { .present = MQS_NOOP, .lines = 1}; unsigned long m; int substatus, ret; struct message_header save_mhdr, *mhdr = mesg; STAT(mesq_noop); save_mhdr = *mhdr; *mhdr = noop_header; gru_mesq(cb, mq, gru_get_tri(mhdr), 1, IMA); ret = gru_wait(cb); if (ret) { substatus = gru_get_cb_message_queue_substatus(cb); switch (substatus) { case CBSS_NO_ERROR: STAT(mesq_noop_unexpected_error); ret = MQE_UNEXPECTED_CB_ERR; break; case CBSS_LB_OVERFLOWED: STAT(mesq_noop_lb_overflow); ret = MQE_CONGESTION; break; case CBSS_QLIMIT_REACHED: STAT(mesq_noop_qlimit_reached); ret = 0; break; case CBSS_AMO_NACKED: STAT(mesq_noop_amo_nacked); ret = MQE_CONGESTION; break; case CBSS_PUT_NACKED: STAT(mesq_noop_put_nacked); m = mq + (gru_get_amo_value_head(cb) << 6); gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, 1, 1, IMA); if (gru_wait(cb) == CBS_IDLE) ret = MQIE_AGAIN; else ret = MQE_UNEXPECTED_CB_ERR; break; case CBSS_PAGE_OVERFLOW: default: BUG(); } } *mhdr = save_mhdr; return ret; } /* * Handle a gru_mesq full. */ static int send_message_queue_full(void *cb, unsigned long mq, void *mesg, int lines) { union gru_mesqhead mqh; unsigned int limit, head; unsigned long avalue; int half, qlines, save; /* Determine if switching to first/second half of q */ avalue = gru_get_amo_value(cb); head = gru_get_amo_value_head(cb); limit = gru_get_amo_value_limit(cb); /* * Fetch "qlines" from the queue header. Since the queue may be * in memory that can't be accessed using socket addresses, use * the GRU to access the data. Use DSR space from the message. */ save = *(int *)mesg; gru_vload(cb, QLINES(mq), gru_get_tri(mesg), XTYPE_W, 1, 1, IMA); if (gru_wait(cb) != CBS_IDLE) goto cberr; qlines = *(int *)mesg; *(int *)mesg = save; half = (limit != qlines); if (half) mqh = gru_mesq_head(qlines / 2 + 1, qlines); else mqh = gru_mesq_head(2, qlines / 2 + 1); /* Try to get lock for switching head pointer */ gru_gamir(cb, EOP_IR_CLR, HSTATUS(mq, half), XTYPE_DW, IMA); if (gru_wait(cb) != CBS_IDLE) goto cberr; if (!gru_get_amo_value(cb)) { STAT(mesq_qf_locked); return MQE_QUEUE_FULL; } /* Got the lock. Send optional NOP if queue not full, */ if (head != limit) { if (send_noop_message(cb, mq, mesg)) { gru_gamir(cb, EOP_IR_INC, HSTATUS(mq, half), XTYPE_DW, IMA); if (gru_wait(cb) != CBS_IDLE) goto cberr; STAT(mesq_qf_noop_not_full); return MQIE_AGAIN; } avalue++; } /* Then flip queuehead to other half of queue. */ gru_gamer(cb, EOP_ERR_CSWAP, mq, XTYPE_DW, mqh.val, avalue, IMA); if (gru_wait(cb) != CBS_IDLE) goto cberr; /* If not successfully in swapping queue head, clear the hstatus lock */ if (gru_get_amo_value(cb) != avalue) { STAT(mesq_qf_switch_head_failed); gru_gamir(cb, EOP_IR_INC, HSTATUS(mq, half), XTYPE_DW, IMA); if (gru_wait(cb) != CBS_IDLE) goto cberr; } return MQIE_AGAIN; cberr: STAT(mesq_qf_unexpected_error); return MQE_UNEXPECTED_CB_ERR; } /* * Handle a gru_mesq failure. Some of these failures are software recoverable * or retryable. */ static int send_message_failure(void *cb, unsigned long mq, void *mesg, int lines) { int substatus, ret = 0; unsigned long m; substatus = gru_get_cb_message_queue_substatus(cb); switch (substatus) { case CBSS_NO_ERROR: STAT(mesq_send_unexpected_error); ret = MQE_UNEXPECTED_CB_ERR; break; case CBSS_LB_OVERFLOWED: STAT(mesq_send_lb_overflow); ret = MQE_CONGESTION; break; case CBSS_QLIMIT_REACHED: STAT(mesq_send_qlimit_reached); ret = send_message_queue_full(cb, mq, mesg, lines); break; case CBSS_AMO_NACKED: STAT(mesq_send_amo_nacked); ret = MQE_CONGESTION; break; case CBSS_PUT_NACKED: STAT(mesq_send_put_nacked); m =mq + (gru_get_amo_value_head(cb) << 6); gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA); if (gru_wait(cb) == CBS_IDLE) ret = MQE_OK; else ret = MQE_UNEXPECTED_CB_ERR; break; default: BUG(); } return ret; }