/** * low-level thread entry point. * * @param[in] rock opaque pointer to thread worker object * * @return opaque return pointer from pool entry function * * @internal */ static void * _afs_tp_worker_run(void * rock) { struct afs_thread_pool_worker * worker = rock; struct afs_thread_pool * pool = worker->pool; /* register worker with pool */ MUTEX_ENTER(&pool->lock); queue_Append(&pool->thread_list, worker); pool->nthreads++; MUTEX_EXIT(&pool->lock); /* call high-level entry point */ worker->ret = (*pool->entry)(pool, worker, pool->work_queue, pool->rock); /* adjust pool live thread count */ MUTEX_ENTER(&pool->lock); osi_Assert(pool->nthreads); queue_Remove(worker); pool->nthreads--; if (!pool->nthreads) { CV_BROADCAST(&pool->shutdown_cv); pool->state = AFS_TP_STATE_STOPPED; } MUTEX_EXIT(&pool->lock); _afs_tp_worker_free(worker); return NULL; }
/** * wakeup all threads waiting in dequeue. * * @param[in] list list object * * @return operation status * @retval 0 success * * @internal */ static int _afs_wq_node_list_shutdown(struct afs_work_queue_node_list * list) { int ret = 0; struct afs_work_queue_node *node, *nnode; MUTEX_ENTER(&list->lock); list->shutdown = 1; for (queue_Scan(&list->list, node, nnode, afs_work_queue_node)) { _afs_wq_node_state_change(node, AFS_WQ_NODE_STATE_ERROR); queue_Remove(node); node->qidx = AFS_WQ_NODE_LIST_NONE; node->queue = NULL; if (node->detached) { /* if we are detached, we hold the reference on the node; * otherwise, it is some other caller that holds the reference. * So don't put the node if we are not detached; the node will * get freed when someone else calls afs_wq_node_put */ afs_wq_node_put(node); } } CV_BROADCAST(&list->cv); MUTEX_EXIT(&list->lock); return ret; }
/* * thread to combine salvager child logs * back into the main salvageserver log */ static void * SalvageLogCleanupThread(void * arg) { struct log_cleanup_node * cleanup; assert(pthread_mutex_lock(&worker_lock) == 0); while (1) { while (queue_IsEmpty(&log_cleanup_queue)) { assert(pthread_cond_wait(&log_cleanup_queue.queue_change_cv, &worker_lock) == 0); } while (queue_IsNotEmpty(&log_cleanup_queue)) { cleanup = queue_First(&log_cleanup_queue, log_cleanup_node); queue_Remove(cleanup); assert(pthread_mutex_unlock(&worker_lock) == 0); SalvageLogCleanup(cleanup->pid); free(cleanup); assert(pthread_mutex_lock(&worker_lock) == 0); } } assert(pthread_mutex_unlock(&worker_lock) == 0); return NULL; }
/* * thread to combine salvager child logs * back into the main salvageserver log */ static void * SalvageLogCleanupThread(void * arg) { struct log_cleanup_node * cleanup; MUTEX_ENTER(&worker_lock); while (1) { while (queue_IsEmpty(&log_cleanup_queue)) { CV_WAIT(&log_cleanup_queue.queue_change_cv, &worker_lock); } while (queue_IsNotEmpty(&log_cleanup_queue)) { cleanup = queue_First(&log_cleanup_queue, log_cleanup_node); queue_Remove(cleanup); MUTEX_EXIT(&worker_lock); SalvageLogCleanup(cleanup->pid); free(cleanup); MUTEX_ENTER(&worker_lock); } } MUTEX_EXIT(&worker_lock); return NULL; }
/* * Process all events that have expired relative to the current clock time * (which is not re-evaluated unless clock_NewTime has been called). * The relative time to the next event is returned in the output parameter * next and the function returns 1. If there are is no next event, * the function returns 0. */ int rxevent_RaiseEvents(struct clock * next) { struct rxevent *qe; struct clock now; #ifdef RXDEBUG if (Log) fprintf(Log, "rxevent_RaiseEvents(%ld.%ld)\n", now.sec, now.usec); #endif /* * Events are sorted by time, so only scan until an event is found that * has not yet timed out */ while (queue_IsNotEmpty(&rxevent_queue)) { clock_GetTime(&now); qe = queue_First(&rxevent_queue, rxevent); if (clock_Lt(&now, &qe->eventTime)) { *next = qe->eventTime; clock_Sub(next, &now); return 1; } queue_Remove(qe); rxevent_nPosted--; qe->func(qe, qe->arg, qe->arg1); queue_Append(&rxevent_free, qe); rxevent_nFree++; } return 0; }
static void cleanup_pthread_cache(void) { thread_p cur = NULL, next = NULL; if (pthread_cache_done) { for(queue_Scan(&active_Q, cur, next, thread)) { queue_Remove(cur); } for(queue_Scan(&cache_Q, cur, next, thread)) { queue_Remove(cur); } pthread_mutex_destroy(&active_Q_mutex); pthread_mutex_destroy(&cache_Q_mutex); pthread_cache_done = 0; } }
/** * remove a node from a list. * * @param[in] node node object * @param[in] next_state node state following successful dequeue * * @return operation status * @retval 0 success * @retval AFS_WQ_ERROR in any of the following conditions: * - node not associated with a work queue * - node was not on a linked list (e.g. RUNNING state) * - we raced another thread * * @pre node->lock held * * @post node removed from node list * * @note node->lock may be dropped internally * * @internal */ static int _afs_wq_node_list_remove(struct afs_work_queue_node * node, afs_wq_work_state_t next_state) { int code, ret = 0; struct afs_work_queue_node_list * list = NULL; _afs_wq_node_state_wait_busy(node); if (!node->queue) { ret = AFS_WQ_ERROR; goto error; } switch (node->qidx) { case AFS_WQ_NODE_LIST_READY: list = &node->queue->ready_list; break; case AFS_WQ_NODE_LIST_BLOCKED: list = &node->queue->blocked_list; break; case AFS_WQ_NODE_LIST_DONE: list = &node->queue->done_list; break; default: ret = AFS_WQ_ERROR; } if (list) { code = MUTEX_TRYENTER(&list->lock); if (!code) { /* contended */ _afs_wq_node_state_change(node, AFS_WQ_NODE_STATE_BUSY); MUTEX_EXIT(&node->lock); MUTEX_ENTER(&list->lock); MUTEX_ENTER(&node->lock); if (node->qidx == AFS_WQ_NODE_LIST_NONE) { /* raced */ ret= AFS_WQ_ERROR; goto done_sync; } } queue_Remove(node); node->qidx = AFS_WQ_NODE_LIST_NONE; _afs_wq_node_state_change(node, next_state); done_sync: MUTEX_EXIT(&list->lock); } error: return ret; }
/** * unlink work nodes from a dependency node. * * @param[in] dep dependency node * * @return operation status * @retval 0 success * * @pre * - dep->parent and dep->child are either locked, or are not referenced * by anything else * - caller holds ref on dep->child * - dep->child and dep->parent in quiescent state * * @internal */ static int _afs_wq_dep_unlink_r(struct afs_work_queue_dep_node *dep) { struct afs_work_queue_node *child = dep->child; queue_Remove(&dep->parent_list); dep->child = NULL; dep->parent = NULL; return _afs_wq_node_put_r(child, 0); }
/** * low-level interface to remove an entry from the hash table. * * Does not alter the refcount or worry about the children lists or * anything like that; just removes the hash table entry, frees it, and * that's all. You probably want @see _VVGC_hash_entry_del instead. * * @param[in] hent hash table entry * * @pre VOL_LOCK held * * @return operation status * @retval 0 success * * @internal */ static int _VVGC_hash_entry_unlink(VVGCache_hash_entry_t * hent) { int code; queue_Remove(hent); hent->entry = NULL; hent->volid = 0; code = _VVGC_hash_entry_free(hent); return code; }
/** * delete a VGC entry from the partition's to-delete list. * * When a VGC scan is ocurring, and a volume is removed, but then created * again, we need to ensure that it does not get deleted from being on the * dlist. Call this function whenever adding a new entry to the VGC during * a VGC scan to ensure it doesn't get deleted later. * * @param[in] dp the partition from whose dlist we are deleting * @param[in] parent the parent volumeID of the VGC entry * @param[in] child the child volumeID of the VGC entry * * @return operation status * @retval 0 success * @retval ENOENT the specified VGC entry is not on the dlist * * @pre VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING * * @internal VGC use only * * @see _VVGC_dlist_add_r */ int _VVGC_dlist_del_r(struct DiskPartition64 *dp, VolumeId parent, VolumeId child) { VVGCache_dlist_entry_t *ent; ent = _VVGC_dlist_lookup_r(dp, parent, child); if (!ent) { return ENOENT; } queue_Remove(ent); free(ent); return 0; }
/** * delete all of the entries in the dlist from the VGC. * * Traverses the to-delete list for the specified partition, and deletes * the specified entries from the global VGC. Also deletes the entries from * the dlist itself as it goes along. * * @param[in] dp the partition whose dlist we are flushing */ static void _VVGC_flush_dlist(struct DiskPartition64 *dp) { int i; VVGCache_dlist_entry_t *ent, *nent; for (i = 0; i < VolumeHashTable.Size; i++) { for (queue_Scan(&VVGCache.part[dp->index].dlist_hash_buckets[i], ent, nent, VVGCache_dlist_entry)) { _VVGC_entry_purge_r(dp, ent->parent, ent->child); queue_Remove(ent); free(ent); } } }
/** * dequeue a node from a list object. * * @param[in] list list object * @param[out] node_out address in which to store node object pointer * @param[in] state new node state * @param[in] block permit blocking on cv if asserted * * @return operation status * @retval 0 success * @retval EWOULDBLOCK block not asserted and nothing to dequeue * @retval EINTR blocking wait interrupted by list shutdown * * @post node object returned with node lock held and new state set * * @internal */ static int _afs_wq_node_list_dequeue(struct afs_work_queue_node_list * list, struct afs_work_queue_node ** node_out, afs_wq_work_state_t state, int block) { int ret = 0; struct afs_work_queue_node * node; MUTEX_ENTER(&list->lock); if (list->shutdown) { *node_out = NULL; ret = EINTR; goto done_sync; } if (!block && queue_IsEmpty(&list->list)) { *node_out = NULL; ret = EWOULDBLOCK; goto done_sync; } while (queue_IsEmpty(&list->list)) { if (list->shutdown) { *node_out = NULL; ret = EINTR; goto done_sync; } CV_WAIT(&list->cv, &list->lock); } *node_out = node = queue_First(&list->list, afs_work_queue_node); MUTEX_ENTER(&node->lock); queue_Remove(node); node->qidx = AFS_WQ_NODE_LIST_NONE; _afs_wq_node_state_change(node, state); done_sync: MUTEX_EXIT(&list->lock); return ret; }
/** * look through log_watch_queue, and if any processes are not still * running, hand them off to the SalvageLogCleanupThread * * @param log_watch_queue a queue of PIDs that we should clean up if * that PID has died */ static void ScanLogs(struct rx_queue *log_watch_queue) { struct log_cleanup_node *cleanup, *next; assert(pthread_mutex_lock(&worker_lock) == 0); for (queue_Scan(log_watch_queue, cleanup, next, log_cleanup_node)) { /* if a process is still running, assume it's the salvage process * still going, and keep waiting for it */ if (kill(cleanup->pid, 0) < 0 && errno == ESRCH) { queue_Remove(cleanup); queue_Append(&log_cleanup_queue, cleanup); assert(pthread_cond_signal(&log_cleanup_queue.queue_change_cv) == 0); } } assert(pthread_mutex_unlock(&worker_lock) == 0); }
/** * look through log_watch_queue, and if any processes are not still * running, hand them off to the SalvageLogCleanupThread * * @param log_watch_queue a queue of PIDs that we should clean up if * that PID has died */ static void ScanLogs(struct rx_queue *log_watch_queue) { struct log_cleanup_node *cleanup, *next; MUTEX_ENTER(&worker_lock); for (queue_Scan(log_watch_queue, cleanup, next, log_cleanup_node)) { /* if a process is still running, assume it's the salvage process * still going, and keep waiting for it */ if (kill(cleanup->pid, 0) < 0 && errno == ESRCH) { queue_Remove(cleanup); queue_Append(&log_cleanup_queue, cleanup); CV_SIGNAL(&log_cleanup_queue.queue_change_cv); } } MUTEX_EXIT(&worker_lock); }
/* rxi_FillReadVec * * Uses packets in the receive queue to fill in as much of the * current iovec as possible. Does not block if it runs out * of packets to complete the iovec. Return true if an ack packet * was sent, otherwise return false */ int rxi_FillReadVec(struct rx_call *call, afs_uint32 serial) { int didConsume = 0; int didHardAck = 0; unsigned int t; struct rx_packet *rp; struct rx_packet *curp; struct iovec *call_iov; struct iovec *cur_iov = NULL; curp = call->currentPacket; if (curp) { cur_iov = &curp->wirevec[call->curvec]; } call_iov = &call->iov[call->iovNext]; while (!call->error && call->iovNBytes && call->iovNext < call->iovMax) { if (call->nLeft == 0) { /* Get next packet */ if (queue_IsNotEmpty(&call->rq)) { /* Check that next packet available is next in sequence */ rp = queue_First(&call->rq, rx_packet); if (rp->header.seq == call->rnext) { afs_int32 error; struct rx_connection *conn = call->conn; queue_Remove(rp); #ifdef RX_TRACK_PACKETS rp->flags &= ~RX_PKTFLAG_RQ; #endif #ifdef RXDEBUG_PACKET call->rqc--; #endif /* RXDEBUG_PACKET */ /* RXS_CheckPacket called to undo RXS_PreparePacket's * work. It may reduce the length of the packet by up * to conn->maxTrailerSize, to reflect the length of the * data + the header. */ if ((error = RXS_CheckPacket(conn->securityObject, call, rp))) { /* Used to merely shut down the call, but now we * shut down the whole connection since this may * indicate an attempt to hijack it */ MUTEX_EXIT(&call->lock); rxi_ConnectionError(conn, error); MUTEX_ENTER(&conn->conn_data_lock); rp = rxi_SendConnectionAbort(conn, rp, 0, 0); MUTEX_EXIT(&conn->conn_data_lock); rxi_FreePacket(rp); MUTEX_ENTER(&call->lock); return 1; } call->rnext++; curp = call->currentPacket = rp; #ifdef RX_TRACK_PACKETS call->currentPacket->flags |= RX_PKTFLAG_CP; #endif call->curvec = 1; /* 0th vec is always header */ cur_iov = &curp->wirevec[1]; /* begin at the beginning [ more or less ], continue * on until the end, then stop. */ call->curpos = (char *)curp->wirevec[1].iov_base + call->conn->securityHeaderSize; call->curlen = curp->wirevec[1].iov_len - call->conn->securityHeaderSize; /* Notice that this code works correctly if the data * size is 0 (which it may be--no reply arguments from * server, for example). This relies heavily on the * fact that the code below immediately frees the packet * (no yields, etc.). If it didn't, this would be a * problem because a value of zero for call->nLeft * normally means that there is no read packet */ call->nLeft = curp->length; hadd32(call->bytesRcvd, curp->length); /* Send a hard ack for every rxi_HardAckRate+1 packets * consumed. Otherwise schedule an event to send * the hard ack later on. */ call->nHardAcks++; didConsume = 1; continue; } } break; } /* It's possible for call->nLeft to be smaller than any particular * iov_len. Usually, recvmsg doesn't change the iov_len, since it * reflects the size of the buffer. We have to keep track of the * number of bytes read in the length field of the packet struct. On * the final portion of a received packet, it's almost certain that * call->nLeft will be smaller than the final buffer. */ while (call->iovNBytes && call->iovNext < call->iovMax && curp) { t = MIN((int)call->curlen, call->iovNBytes); t = MIN(t, (int)call->nLeft); call_iov->iov_base = call->curpos; call_iov->iov_len = t; call_iov++; call->iovNext++; call->iovNBytes -= t; call->curpos += t; call->curlen -= t; call->nLeft -= t; if (!call->nLeft) { /* out of packet. Get another one. */ #ifdef RX_TRACK_PACKETS curp->flags &= ~RX_PKTFLAG_CP; curp->flags |= RX_PKTFLAG_IOVQ; #endif queue_Append(&call->iovq, curp); #ifdef RXDEBUG_PACKET call->iovqc++; #endif /* RXDEBUG_PACKET */ curp = call->currentPacket = (struct rx_packet *)0; } else if (!call->curlen) { /* need to get another struct iov */ if (++call->curvec >= curp->niovecs) { /* current packet is exhausted, get ready for another */ /* don't worry about curvec and stuff, they get set somewhere else */ #ifdef RX_TRACK_PACKETS curp->flags &= ~RX_PKTFLAG_CP; curp->flags |= RX_PKTFLAG_IOVQ; #endif queue_Append(&call->iovq, curp); #ifdef RXDEBUG_PACKET call->iovqc++; #endif /* RXDEBUG_PACKET */ curp = call->currentPacket = (struct rx_packet *)0; call->nLeft = 0; } else { cur_iov++; call->curpos = (char *)cur_iov->iov_base; call->curlen = cur_iov->iov_len; } } } } /* If we consumed any packets then check whether we need to * send a hard ack. */ if (didConsume && (!(call->flags & RX_CALL_RECEIVE_DONE))) { if (call->nHardAcks > (u_short) rxi_HardAckRate) { rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY); rxi_SendAck(call, 0, serial, RX_ACK_DELAY, 0); didHardAck = 1; } else { struct clock when, now; clock_GetTime(&now); when = now; /* Delay to consolidate ack packets */ clock_Add(&when, &rx_hardAckDelay); if (!call->delayedAckEvent || clock_Gt(&call->delayedAckEvent->eventTime, &when)) { rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY); MUTEX_ENTER(&rx_refcnt_mutex); CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY); MUTEX_EXIT(&rx_refcnt_mutex); call->delayedAckEvent = rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0); } } } return didHardAck; }
/* rxi_ReadProc -- internal version. * * LOCKS USED -- called at netpri */ int rxi_ReadProc(struct rx_call *call, char *buf, int nbytes) { struct rx_packet *cp = call->currentPacket; struct rx_packet *rp; int requestCount; unsigned int t; /* XXXX took out clock_NewTime from here. Was it needed? */ requestCount = nbytes; /* Free any packets from the last call to ReadvProc/WritevProc */ if (queue_IsNotEmpty(&call->iovq)) { #ifdef RXDEBUG_PACKET call->iovqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &call->iovq); } do { if (call->nLeft == 0) { /* Get next packet */ MUTEX_ENTER(&call->lock); for (;;) { if (call->error || (call->mode != RX_MODE_RECEIVING)) { if (call->error) { call->mode = RX_MODE_ERROR; MUTEX_EXIT(&call->lock); return 0; } if (call->mode == RX_MODE_SENDING) { MUTEX_EXIT(&call->lock); rxi_FlushWrite(call); MUTEX_ENTER(&call->lock); continue; } } if (queue_IsNotEmpty(&call->rq)) { /* Check that next packet available is next in sequence */ rp = queue_First(&call->rq, rx_packet); if (rp->header.seq == call->rnext) { afs_int32 error; struct rx_connection *conn = call->conn; queue_Remove(rp); #ifdef RX_TRACK_PACKETS rp->flags &= ~RX_PKTFLAG_RQ; #endif #ifdef RXDEBUG_PACKET call->rqc--; #endif /* RXDEBUG_PACKET */ /* RXS_CheckPacket called to undo RXS_PreparePacket's * work. It may reduce the length of the packet by up * to conn->maxTrailerSize, to reflect the length of the * data + the header. */ if ((error = RXS_CheckPacket(conn->securityObject, call, rp))) { /* Used to merely shut down the call, but now we * shut down the whole connection since this may * indicate an attempt to hijack it */ MUTEX_EXIT(&call->lock); rxi_ConnectionError(conn, error); MUTEX_ENTER(&conn->conn_data_lock); rp = rxi_SendConnectionAbort(conn, rp, 0, 0); MUTEX_EXIT(&conn->conn_data_lock); rxi_FreePacket(rp); return 0; } call->rnext++; cp = call->currentPacket = rp; #ifdef RX_TRACK_PACKETS call->currentPacket->flags |= RX_PKTFLAG_CP; #endif call->curvec = 1; /* 0th vec is always header */ /* begin at the beginning [ more or less ], continue * on until the end, then stop. */ call->curpos = (char *)cp->wirevec[1].iov_base + call->conn->securityHeaderSize; call->curlen = cp->wirevec[1].iov_len - call->conn->securityHeaderSize; /* Notice that this code works correctly if the data * size is 0 (which it may be--no reply arguments from * server, for example). This relies heavily on the * fact that the code below immediately frees the packet * (no yields, etc.). If it didn't, this would be a * problem because a value of zero for call->nLeft * normally means that there is no read packet */ call->nLeft = cp->length; hadd32(call->bytesRcvd, cp->length); /* Send a hard ack for every rxi_HardAckRate+1 packets * consumed. Otherwise schedule an event to send * the hard ack later on. */ call->nHardAcks++; if (!(call->flags & RX_CALL_RECEIVE_DONE)) { if (call->nHardAcks > (u_short) rxi_HardAckRate) { rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY); rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0); } else { struct clock when, now; clock_GetTime(&now); when = now; /* Delay to consolidate ack packets */ clock_Add(&when, &rx_hardAckDelay); if (!call->delayedAckEvent || clock_Gt(&call->delayedAckEvent-> eventTime, &when)) { rxevent_Cancel(call->delayedAckEvent, call, RX_CALL_REFCOUNT_DELAY); MUTEX_ENTER(&rx_refcnt_mutex); CALL_HOLD(call, RX_CALL_REFCOUNT_DELAY); MUTEX_EXIT(&rx_refcnt_mutex); call->delayedAckEvent = rxevent_PostNow(&when, &now, rxi_SendDelayedAck, call, 0); } } } break; } } /* * If we reach this point either we have no packets in the * receive queue or the next packet in the queue is not the * one we are looking for. There is nothing else for us to * do but wait for another packet to arrive. */ /* Are there ever going to be any more packets? */ if (call->flags & RX_CALL_RECEIVE_DONE) { MUTEX_EXIT(&call->lock); return requestCount - nbytes; } /* Wait for in-sequence packet */ call->flags |= RX_CALL_READER_WAIT; clock_NewTime(); call->startWait = clock_Sec(); while (call->flags & RX_CALL_READER_WAIT) { #ifdef RX_ENABLE_LOCKS CV_WAIT(&call->cv_rq, &call->lock); #else osi_rxSleep(&call->rq); #endif } cp = call->currentPacket; call->startWait = 0; #ifdef RX_ENABLE_LOCKS if (call->error) { MUTEX_EXIT(&call->lock); return 0; } #endif /* RX_ENABLE_LOCKS */ } MUTEX_EXIT(&call->lock); } else /* osi_Assert(cp); */ /* MTUXXX this should be replaced by some error-recovery code before shipping */ /* yes, the following block is allowed to be the ELSE clause (or not) */ /* It's possible for call->nLeft to be smaller than any particular * iov_len. Usually, recvmsg doesn't change the iov_len, since it * reflects the size of the buffer. We have to keep track of the * number of bytes read in the length field of the packet struct. On * the final portion of a received packet, it's almost certain that * call->nLeft will be smaller than the final buffer. */ while (nbytes && cp) { t = MIN((int)call->curlen, nbytes); t = MIN(t, (int)call->nLeft); memcpy(buf, call->curpos, t); buf += t; nbytes -= t; call->curpos += t; call->curlen -= t; call->nLeft -= t; if (!call->nLeft) { /* out of packet. Get another one. */ #ifdef RX_TRACK_PACKETS call->currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(cp); cp = call->currentPacket = (struct rx_packet *)0; } else if (!call->curlen) { /* need to get another struct iov */ if (++call->curvec >= cp->niovecs) { /* current packet is exhausted, get ready for another */ /* don't worry about curvec and stuff, they get set somewhere else */ #ifdef RX_TRACK_PACKETS call->currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(cp); cp = call->currentPacket = (struct rx_packet *)0; call->nLeft = 0; } else { call->curpos = (char *)cp->wirevec[call->curvec].iov_base; call->curlen = cp->wirevec[call->curvec].iov_len; } } } if (!nbytes) { /* user buffer is full, return */ return requestCount; } } while (nbytes); return requestCount; }
/* Add the indicated event (function, arg) at the specified clock time */ struct rxevent * rxevent_Post(struct clock * when, void (*func)(), void *arg, void *arg1) /* when - When event should happen, in clock (clock.h) units */ { struct rxevent *ev, *qe, *qpr; #ifdef RXDEBUG if (Log) { struct clock now; clock_GetTime(&now); fprintf(Log, "%ld.%ld: rxevent_Post(%ld.%ld, %p, %p)\n", now.sec, now.usec, when->sec, when->usec, func, arg); } #endif #if defined(AFS_SGIMP_ENV) ASSERT(osi_rxislocked()); #endif /* * If we're short on free event entries, create a block of new ones and * add them to the free queue */ if (queue_IsEmpty(&rxevent_free)) { int i; #if defined(AFS_AIX32_ENV) && defined(KERNEL) ev = (struct rxevent *) rxi_Alloc(sizeof(struct rxevent)); queue_Append(&rxevent_free, &ev[0]), rxevent_nFree++; #else ev = (struct rxevent *) osi_Alloc(sizeof(struct rxevent) * rxevent_allocUnit); xsp = xfreemallocs; xfreemallocs = (struct xfreelist *) ev; xfreemallocs->next = xsp; for (i = 0; i < rxevent_allocUnit; i++) queue_Append(&rxevent_free, &ev[i]), rxevent_nFree++; #endif } /* Grab and initialize a new rxevent structure */ ev = queue_First(&rxevent_free, rxevent); queue_Remove(ev); rxevent_nFree--; /* Record user defined event state */ ev->eventTime = *when; ev->func = func; ev->arg = arg; ev->arg1 = arg1; rxevent_nPosted += 1; /* Rather than ++, to shut high-C up * regarding never-set variables */ /* * Locate a slot for the new entry. The queue is ordered by time, and we * assume that a new entry is likely to be greater than a majority of the * entries already on the queue (unless there's very few entries on the * queue), so we scan it backwards */ for (queue_ScanBackwards(&rxevent_queue, qe, qpr, rxevent)) { if (clock_Ge(when, &qe->eventTime)) { queue_InsertAfter(qe, ev); return ev; } } /* The event is to expire earlier than any existing events */ queue_Prepend(&rxevent_queue, ev); if (rxevent_ScheduledEarlierEvent) (*rxevent_ScheduledEarlierEvent) (); /* Notify our external * scheduler */ return ev; }
/* rxi_WritevProc -- internal version. * * Send buffers allocated in rxi_WritevAlloc. * * LOCKS USED -- called at netpri. */ int rxi_WritevProc(struct rx_call *call, struct iovec *iov, int nio, int nbytes) { struct rx_packet *cp = NULL; #ifdef RX_TRACK_PACKETS struct rx_packet *p, *np; #endif int nextio; int requestCount; struct rx_queue tmpq; #ifdef RXDEBUG_PACKET u_short tmpqc; #endif requestCount = nbytes; nextio = 0; MUTEX_ENTER(&call->lock); if (call->error) { call->mode = RX_MODE_ERROR; } else if (call->mode != RX_MODE_SENDING) { call->error = RX_PROTOCOL_ERROR; } #ifdef AFS_GLOBAL_RXLOCK_KERNEL rxi_WaitforTQBusy(call); #endif /* AFS_GLOBAL_RXLOCK_KERNEL */ cp = call->currentPacket; if (call->error) { call->mode = RX_MODE_ERROR; MUTEX_EXIT(&call->lock); if (cp) { #ifdef RX_TRACK_PACKETS cp->flags &= ~RX_PKTFLAG_CP; cp->flags |= RX_PKTFLAG_IOVQ; #endif queue_Prepend(&call->iovq, cp); #ifdef RXDEBUG_PACKET call->iovqc++; #endif /* RXDEBUG_PACKET */ call->currentPacket = (struct rx_packet *)0; } #ifdef RXDEBUG_PACKET call->iovqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &call->iovq); return 0; } /* Loop through the I/O vector adjusting packet pointers. * Place full packets back onto the iovq once they are ready * to send. Set RX_PROTOCOL_ERROR if any problems are found in * the iovec. We put the loop condition at the end to ensure that * a zero length write will push a short packet. */ nextio = 0; queue_Init(&tmpq); #ifdef RXDEBUG_PACKET tmpqc = 0; #endif /* RXDEBUG_PACKET */ do { if (call->nFree == 0 && cp) { clock_NewTime(); /* Bogus: need new time package */ /* The 0, below, specifies that it is not the last packet: * there will be others. PrepareSendPacket may * alter the packet length by up to * conn->securityMaxTrailerSize */ hadd32(call->bytesSent, cp->length); rxi_PrepareSendPacket(call, cp, 0); queue_Append(&tmpq, cp); #ifdef RXDEBUG_PACKET tmpqc++; #endif /* RXDEBUG_PACKET */ cp = call->currentPacket = (struct rx_packet *)0; /* The head of the iovq is now the current packet */ if (nbytes) { if (queue_IsEmpty(&call->iovq)) { MUTEX_EXIT(&call->lock); call->error = RX_PROTOCOL_ERROR; #ifdef RXDEBUG_PACKET tmpqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &tmpq); return 0; } cp = queue_First(&call->iovq, rx_packet); queue_Remove(cp); #ifdef RX_TRACK_PACKETS cp->flags &= ~RX_PKTFLAG_IOVQ; #endif #ifdef RXDEBUG_PACKET call->iovqc--; #endif /* RXDEBUG_PACKET */ #ifdef RX_TRACK_PACKETS cp->flags |= RX_PKTFLAG_CP; #endif call->currentPacket = cp; call->nFree = cp->length; call->curvec = 1; call->curpos = (char *)cp->wirevec[1].iov_base + call->conn->securityHeaderSize; call->curlen = cp->wirevec[1].iov_len - call->conn->securityHeaderSize; } } if (nbytes) { /* The next iovec should point to the current position */ if (iov[nextio].iov_base != call->curpos || iov[nextio].iov_len > (int)call->curlen) { call->error = RX_PROTOCOL_ERROR; MUTEX_EXIT(&call->lock); if (cp) { #ifdef RX_TRACK_PACKETS cp->flags &= ~RX_PKTFLAG_CP; #endif queue_Prepend(&tmpq, cp); #ifdef RXDEBUG_PACKET tmpqc++; #endif /* RXDEBUG_PACKET */ cp = call->currentPacket = (struct rx_packet *)0; } #ifdef RXDEBUG_PACKET tmpqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &tmpq); return 0; } nbytes -= iov[nextio].iov_len; call->curpos += iov[nextio].iov_len; call->curlen -= iov[nextio].iov_len; call->nFree -= iov[nextio].iov_len; nextio++; if (call->curlen == 0) { if (++call->curvec > cp->niovecs) { call->nFree = 0; } else { call->curpos = (char *)cp->wirevec[call->curvec].iov_base; call->curlen = cp->wirevec[call->curvec].iov_len; } } } } while (nbytes && nextio < nio); /* Move the packets from the temporary queue onto the transmit queue. * We may end up with more than call->twind packets on the queue. */ #ifdef RX_TRACK_PACKETS for (queue_Scan(&tmpq, p, np, rx_packet)) { p->flags |= RX_PKTFLAG_TQ; } #endif if (call->error) call->mode = RX_MODE_ERROR; queue_SpliceAppend(&call->tq, &tmpq); if (!(call->flags & (RX_CALL_FAST_RECOVER | RX_CALL_FAST_RECOVER_WAIT))) { rxi_Start(0, call, 0, 0); } /* Wait for the length of the transmit queue to fall below call->twind */ while (!call->error && call->tnext + 1 > call->tfirst + (2 * call->twind)) { clock_NewTime(); call->startWait = clock_Sec(); #ifdef RX_ENABLE_LOCKS CV_WAIT(&call->cv_twind, &call->lock); #else call->flags |= RX_CALL_WAIT_WINDOW_ALLOC; osi_rxSleep(&call->twind); #endif call->startWait = 0; } /* cp is no longer valid since we may have given up the lock */ cp = call->currentPacket; if (call->error) { call->mode = RX_MODE_ERROR; call->currentPacket = NULL; MUTEX_EXIT(&call->lock); if (cp) { #ifdef RX_TRACK_PACKETS cp->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(cp); } return 0; } MUTEX_EXIT(&call->lock); return requestCount - nbytes; }