/** * wait for all pending nodes to finish. * * @param[in] queue work queue * * @return operation status * @retval 0 success * * @post the specified queue was empty at some point; it may not be empty by * the time this function returns, but at some point after the function was * called, there were no nodes in the ready queue or blocked queue. */ int afs_wq_wait_all(struct afs_work_queue *queue) { int ret = 0; MUTEX_ENTER(&queue->lock); while (queue->pend_count > 0 && !queue->shutdown) { CV_WAIT(&queue->empty_cv, &queue->lock); } if (queue->shutdown) { /* queue has been shut down, but there may still be some threads * running e.g. in the middle of their callback. ensure they have * stopped before we return. */ while (queue->running_count > 0) { CV_WAIT(&queue->running_cv, &queue->lock); } ret = EINTR; goto done; } done: MUTEX_EXIT(&queue->lock); /* technically this doesn't really guarantee that the work queue is empty * after we return, but we do guarantee that it was empty at some point */ return ret; }
/* Return the user's connection index of the most recently ready call; that is, a call that has received at least one reply packet */ int multi_Select(struct multi_handle *mh) { int index; SPLVAR; NETPRI; #ifdef RX_ENABLE_LOCKS MUTEX_ENTER(&mh->lock); #endif /* RX_ENABLE_LOCKS */ while (mh->nextReady == mh->firstNotReady) { if (mh->nReady == mh->nConns) { #ifdef RX_ENABLE_LOCKS MUTEX_EXIT(&mh->lock); #endif /* RX_ENABLE_LOCKS */ USERPRI; return -1; } #ifdef RX_ENABLE_LOCKS CV_WAIT(&mh->cv, &mh->lock); #else /* RX_ENABLE_LOCKS */ osi_rxSleep(mh); #endif /* RX_ENABLE_LOCKS */ } index = *(mh->nextReady); (mh->nextReady) += 1; #ifdef RX_ENABLE_LOCKS MUTEX_EXIT(&mh->lock); #endif /* RX_ENABLE_LOCKS */ USERPRI; return index; }
afs_int32 canWrite(int fid) { #ifndef AFS_PTHREAD_ENV afs_int32 code = 0; #endif extern dumpSyncP dumpSyncPtr; ObtainWriteLock(&dumpSyncPtr->ds_lock); /* let the pipe drain */ while (dumpSyncPtr->ds_bytes > 0) { if (dumpSyncPtr->ds_readerStatus == DS_WAITING) { dumpSyncPtr->ds_readerStatus = 0; #ifdef AFS_PTHREAD_ENV CV_BROADCAST(&dumpSyncPtr->ds_readerStatus_cond); #else code = LWP_SignalProcess(&dumpSyncPtr->ds_readerStatus); if (code) LogError(code, "canWrite: Signal delivery failed\n"); #endif } dumpSyncPtr->ds_writerStatus = DS_WAITING; ReleaseWriteLock(&dumpSyncPtr->ds_lock); #ifdef AFS_PTHREAD_ENV MUTEX_ENTER(&dumpSyncPtr->ds_writerStatus_mutex); CV_WAIT(&dumpSyncPtr->ds_writerStatus_cond, &dumpSyncPtr->ds_writerStatus_mutex); MUTEX_EXIT(&dumpSyncPtr->ds_writerStatus_mutex); #else LWP_WaitProcess(&dumpSyncPtr->ds_writerStatus); #endif ObtainWriteLock(&dumpSyncPtr->ds_lock); } return (1); }
/* * 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; }
/** * wait for a node to complete; dequeue from done list. * * @param[in] node work queue node * @param[out] retcode return code from work unit * * @return operation status * @retval 0 sucess * * @pre ref held on node */ int afs_wq_node_wait(struct afs_work_queue_node * node, int * retcode) { int ret = 0; MUTEX_ENTER(&node->lock); if (node->state == AFS_WQ_NODE_STATE_INIT) { /* not sure what to do in this case */ goto done_sync; } while ((node->state != AFS_WQ_NODE_STATE_DONE) && (node->state != AFS_WQ_NODE_STATE_ERROR)) { CV_WAIT(&node->state_cv, &node->lock); } if (retcode) { *retcode = node->retcode; } if (node->queue == NULL) { /* nothing we can do */ goto done_sync; } ret = _afs_wq_node_list_remove(node, AFS_WQ_NODE_STATE_INIT); done_sync: MUTEX_EXIT(&node->lock); return ret; }
/** * wait for a node's state to change from busy to something else. * * @param[in] node node object * * @return operation status * @retval 0 success * * @pre node->lock held * * @internal */ static int _afs_wq_node_state_wait_busy(struct afs_work_queue_node * node) { while (node->state == AFS_WQ_NODE_STATE_BUSY) { CV_WAIT(&node->state_cv, &node->lock); } return 0; }
void addHPSStransaction() { HPSS_LOCK; while (waiting) { waiters++; CV_WAIT(&auth_cond, &rxosd_hpss_mutex); waiters--; } HPSStransactions++; HPSS_UNLOCK; }
static void * SalvageChildReaperThread(void * args) { int slot, pid, status; struct log_cleanup_node * cleanup; MUTEX_ENTER(&worker_lock); /* loop reaping our children */ while (1) { /* wait() won't block unless we have children, so * block on the cond var if we're childless */ while (current_workers == 0) { CV_WAIT(&worker_cv, &worker_lock); } MUTEX_EXIT(&worker_lock); cleanup = (struct log_cleanup_node *) malloc(sizeof(struct log_cleanup_node)); while (Reap_Child("salvageserver", &pid, &status) < 0) { /* try to prevent livelock if something goes wrong */ sleep(1); } VOL_LOCK; for (slot = 0; slot < Parallel; slot++) { if (child_slot[slot] == pid) break; } osi_Assert(slot < Parallel); child_slot[slot] = 0; VOL_UNLOCK; SALVSYNC_doneWorkByPid(pid, status); MUTEX_ENTER(&worker_lock); if (cleanup) { cleanup->pid = pid; queue_Append(&log_cleanup_queue, cleanup); CV_SIGNAL(&log_cleanup_queue.queue_change_cv); } /* ok, we've reaped a child */ current_workers--; CV_BROADCAST(&worker_cv); } return NULL; }
/** * shut down all threads in pool. * * @param[in] pool thread pool object * @param[in] block wait for all threads to terminate, if asserted * * @return operation status * @retval 0 success */ int afs_tp_shutdown(struct afs_thread_pool * pool, int block) { int ret = 0; struct afs_thread_pool_worker * worker, *nn; MUTEX_ENTER(&pool->lock); if (pool->state == AFS_TP_STATE_STOPPED || pool->state == AFS_TP_STATE_STOPPING) { goto done_stopped; } if (pool->state != AFS_TP_STATE_RUNNING) { ret = AFS_TP_ERROR; goto done_sync; } pool->state = AFS_TP_STATE_STOPPING; for (queue_Scan(&pool->thread_list, worker, nn, afs_thread_pool_worker)) { worker->req_shutdown = 1; } if (!pool->nthreads) { pool->state = AFS_TP_STATE_STOPPED; } /* need to drop lock to get a membar here */ MUTEX_EXIT(&pool->lock); ret = afs_wq_shutdown(pool->work_queue); if (ret) { goto error; } MUTEX_ENTER(&pool->lock); done_stopped: if (block) { while (pool->nthreads) { CV_WAIT(&pool->shutdown_cv, &pool->lock); } } done_sync: MUTEX_EXIT(&pool->lock); error: return ret; }
/** * 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; }
void doneWriting(afs_int32 error) { #ifndef AFS_PTHREAD_ENV afs_int32 code = 0; #endif /* wait for the reader */ ObtainWriteLock(&dumpSyncPtr->ds_lock); while (dumpSyncPtr->ds_readerStatus != DS_WAITING) { LogDebug(4, "doneWriting: waiting for Reader\n"); dumpSyncPtr->ds_writerStatus = DS_WAITING; ReleaseWriteLock(&dumpSyncPtr->ds_lock); #ifdef AFS_PTHREAD_ENV MUTEX_ENTER(&dumpSyncPtr->ds_writerStatus_mutex); CV_WAIT(&dumpSyncPtr->ds_writerStatus_cond, &dumpSyncPtr->ds_writerStatus_mutex); MUTEX_EXIT(&dumpSyncPtr->ds_writerStatus_mutex); #else LWP_WaitProcess(&dumpSyncPtr->ds_writerStatus); #endif ObtainWriteLock(&dumpSyncPtr->ds_lock); } LogDebug(4, "doneWriting: setting done\n"); /* signal that we are done */ if (error) dumpSyncPtr->ds_writerStatus = DS_DONE_ERROR; else dumpSyncPtr->ds_writerStatus = DS_DONE; dumpSyncPtr->ds_readerStatus = 0; #ifdef AFS_PTHREAD_ENV CV_BROADCAST(&dumpSyncPtr->ds_readerStatus_cond); #else code = LWP_NoYieldSignal(&dumpSyncPtr->ds_readerStatus); if (code) LogError(code, "doneWriting: Signal delivery failed\n"); #endif ReleaseWriteLock(&dumpSyncPtr->ds_lock); }
/** * acquire a lock on a file on local disk. * * @param[in] dl the VDiskLock structure corresponding to the file on disk * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if * you want a write lock * @param[in] nonblock 0 to wait for conflicting locks to clear before * obtaining the lock; 1 to fail immediately if a * conflicting lock is held by someone else * * @return operation status * @retval 0 success * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was * specified * @retval EIO error acquiring file lock * * @note DAFS only * * @note while normal fcntl-y locks on Unix systems generally only work per- * process, this interface also deals with locks between threads in the * process in addition to different processes acquiring the lock */ int VGetDiskLock(struct VDiskLock *dl, int locktype, int nonblock) { int code = 0; osi_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK); if (nonblock) { if (locktype == READ_LOCK) { ObtainReadLockNoBlock(&dl->rwlock, code); } else { ObtainWriteLockNoBlock(&dl->rwlock, code); } if (code) { return EBUSY; } } else if (locktype == READ_LOCK) { ObtainReadLock(&dl->rwlock); } else { ObtainWriteLock(&dl->rwlock); } MUTEX_ENTER(&dl->mutex); if ((dl->flags & VDISKLOCK_ACQUIRING)) { /* Some other thread is waiting to acquire an fs lock. If nonblock=1, * we can return immediately, since we know we'll need to wait to * acquire. Otherwise, wait for the other thread to finish acquiring * the fs lock */ if (nonblock) { code = EBUSY; } else { while ((dl->flags & VDISKLOCK_ACQUIRING)) { CV_WAIT(&dl->cv, &dl->mutex); } } } if (code == 0 && !(dl->flags & VDISKLOCK_ACQUIRED)) { /* no other thread holds the lock on the actual file; so grab one */ /* first try, don't block on the lock to see if we can get it without * waiting */ code = VLockFileLock(dl->lockfile, dl->offset, locktype, 1); if (code == EBUSY && !nonblock) { /* mark that we are waiting on the fs lock */ dl->flags |= VDISKLOCK_ACQUIRING; MUTEX_EXIT(&dl->mutex); code = VLockFileLock(dl->lockfile, dl->offset, locktype, nonblock); MUTEX_ENTER(&dl->mutex); dl->flags &= ~VDISKLOCK_ACQUIRING; if (code == 0) { dl->flags |= VDISKLOCK_ACQUIRED; } CV_BROADCAST(&dl->cv); } } if (code) { if (locktype == READ_LOCK) { ReleaseReadLock(&dl->rwlock); } else { ReleaseWriteLock(&dl->rwlock); } } else { /* successfully got the lock, so inc the number of unlocks we need * to do before we can unlock the actual file */ ++dl->lockers; } MUTEX_EXIT(&dl->mutex); return code; }
/** * schedule a work node for execution. * * @param[in] queue work queue * @param[in] node work node * @param[in] opts options for adding, or NULL for defaults * * @return operation status * @retval 0 success * @retval EWOULDBLOCK queue is full and opts specified not to block * @retval EINTR queue was full, we blocked to add, and the queue was * shutdown while we were blocking */ int afs_wq_add(struct afs_work_queue *queue, struct afs_work_queue_node *node, struct afs_work_queue_add_opts *opts) { int ret = 0; int donate, block, force, hithresh; struct afs_work_queue_node_list * list; struct afs_work_queue_add_opts l_opts; int waited_for_drain = 0; afs_wq_work_state_t state; if (!opts) { afs_wq_add_opts_init(&l_opts); opts = &l_opts; } donate = opts->donate; block = opts->block; force = opts->force; retry: MUTEX_ENTER(&node->lock); ret = _afs_wq_node_state_wait_busy(node); if (ret) { goto error; } if (!node->block_count && !node->error_count) { list = &queue->ready_list; state = AFS_WQ_NODE_STATE_SCHEDULED; } else if (node->error_count) { list = &queue->done_list; state = AFS_WQ_NODE_STATE_ERROR; } else { list = &queue->blocked_list; state = AFS_WQ_NODE_STATE_BLOCKED; } ret = 0; MUTEX_ENTER(&queue->lock); if (queue->shutdown) { ret = EINTR; MUTEX_EXIT(&queue->lock); MUTEX_EXIT(&node->lock); goto error; } hithresh = queue->opts.pend_hithresh; if (hithresh > 0 && queue->pend_count >= hithresh) { queue->drain = 1; } if (!force && (state == AFS_WQ_NODE_STATE_SCHEDULED || state == AFS_WQ_NODE_STATE_BLOCKED)) { if (queue->drain) { if (block) { MUTEX_EXIT(&node->lock); CV_WAIT(&queue->pend_cv, &queue->lock); if (queue->shutdown) { ret = EINTR; } else { MUTEX_EXIT(&queue->lock); waited_for_drain = 1; goto retry; } } else { ret = EWOULDBLOCK; } } } if (ret == 0) { queue->pend_count++; } if (waited_for_drain) { /* signal another thread that may have been waiting for drain */ CV_SIGNAL(&queue->pend_cv); } MUTEX_EXIT(&queue->lock); if (ret) { goto error; } if (!donate) node->refcount++; node->queue = queue; ret = _afs_wq_node_list_enqueue(list, node, state); error: return ret; }
/* 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; }
static void SalvageServer(int argc, char **argv) { int pid, ret; struct SalvageQueueNode * node; pthread_t tid; pthread_attr_t attrs; int slot; VolumePackageOptions opts; /* All entries to the log will be appended. Useful if there are * multiple salvagers appending to the log. */ CheckLogFile((char *)AFSDIR_SERVER_SALSRVLOG_FILEPATH); #ifndef AFS_NT40_ENV #ifdef AFS_LINUX20_ENV fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */ #else fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */ #endif #endif setlinebuf(logFile); fprintf(logFile, "%s\n", cml_version_number); LogCommandLine(argc, argv, "Online Salvage Server", SalvageVersion, "Starting OpenAFS", Log); /* Get and hold a lock for the duration of the salvage to make sure * that no other salvage runs at the same time. The routine * VInitVolumePackage2 (called below) makes sure that a file server or * other volume utilities don't interfere with the salvage. */ /* even demand attach online salvager * still needs this because we don't want * a stand-alone salvager to conflict with * the salvager daemon */ ObtainSharedSalvageLock(); child_slot = (int *) malloc(Parallel * sizeof(int)); osi_Assert(child_slot != NULL); memset(child_slot, 0, Parallel * sizeof(int)); /* initialize things */ VOptDefaults(salvageServer, &opts); if (VInitVolumePackage2(salvageServer, &opts)) { Log("Shutting down: errors encountered initializing volume package\n"); Exit(1); } DInit(10); queue_Init(&pending_q); queue_Init(&log_cleanup_queue); MUTEX_INIT(&worker_lock, "worker", MUTEX_DEFAULT, 0); CV_INIT(&worker_cv, "worker", CV_DEFAULT, 0); CV_INIT(&log_cleanup_queue.queue_change_cv, "queuechange", CV_DEFAULT, 0); osi_Assert(pthread_attr_init(&attrs) == 0); /* start up the reaper and log cleaner threads */ osi_Assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0); osi_Assert(pthread_create(&tid, &attrs, &SalvageChildReaperThread, NULL) == 0); osi_Assert(pthread_create(&tid, &attrs, &SalvageLogCleanupThread, NULL) == 0); osi_Assert(pthread_create(&tid, &attrs, &SalvageLogScanningThread, NULL) == 0); /* loop forever serving requests */ while (1) { node = SALVSYNC_getWork(); osi_Assert(node != NULL); Log("dispatching child to salvage volume %u...\n", node->command.sop.parent); VOL_LOCK; /* find a slot */ for (slot = 0; slot < Parallel; slot++) { if (!child_slot[slot]) break; } osi_Assert (slot < Parallel); do_fork: pid = Fork(); if (pid == 0) { VOL_UNLOCK; ret = DoSalvageVolume(node, slot); Exit(ret); } else if (pid < 0) { Log("failed to fork child worker process\n"); sleep(1); goto do_fork; } else { child_slot[slot] = pid; node->pid = pid; VOL_UNLOCK; MUTEX_ENTER(&worker_lock); current_workers++; /* let the reaper thread know another worker was spawned */ CV_BROADCAST(&worker_cv); /* if we're overquota, wait for the reaper */ while (current_workers >= Parallel) { CV_WAIT(&worker_cv, &worker_lock); } MUTEX_EXIT(&worker_lock); } } }
int rxi_WriteProc(struct rx_call *call, char *buf, int nbytes) { struct rx_connection *conn = call->conn; unsigned int t; int requestCount = nbytes; /* Free any packets from the last call to ReadvProc/WritevProc */ if (!opr_queue_IsEmpty(&call->app.iovq)) { #ifdef RXDEBUG_PACKET call->iovqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &call->app.iovq); } if (call->app.mode != RX_MODE_SENDING) { if ((conn->type == RX_SERVER_CONNECTION) && (call->app.mode == RX_MODE_RECEIVING)) { call->app.mode = RX_MODE_SENDING; if (call->app.currentPacket) { #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(call->app.currentPacket); call->app.currentPacket = NULL; call->app.nLeft = 0; call->app.nFree = 0; } } else { return 0; } } /* Loop condition is checked at end, so that a write of 0 bytes * will force a packet to be created--specially for the case where * there are 0 bytes on the stream, but we must send a packet * anyway. */ do { if (call->app.nFree == 0) { MUTEX_ENTER(&call->lock); if (call->error) call->app.mode = RX_MODE_ERROR; if (!call->error && call->app.currentPacket) { 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 */ call->app.bytesSent += call->app.currentPacket->length; rxi_PrepareSendPacket(call, call->app.currentPacket, 0); /* PrepareSendPacket drops the call lock */ rxi_WaitforTQBusy(call); #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags |= RX_PKTFLAG_TQ; #endif opr_queue_Append(&call->tq, &call->app.currentPacket->entry); #ifdef RXDEBUG_PACKET call->tqc++; #endif /* RXDEBUG_PACKET */ #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif call->app.currentPacket = NULL; /* If the call is in recovery, let it exhaust its current * retransmit queue before forcing it to send new packets */ if (!(call->flags & (RX_CALL_FAST_RECOVER))) { rxi_Start(call, 0); } } else if (call->app.currentPacket) { #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(call->app.currentPacket); call->app.currentPacket = NULL; } /* Wait for transmit window to open up */ 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; #ifdef RX_ENABLE_LOCKS if (call->error) { call->app.mode = RX_MODE_ERROR; MUTEX_EXIT(&call->lock); return 0; } #endif /* RX_ENABLE_LOCKS */ } if ((call->app.currentPacket = rxi_AllocSendPacket(call, nbytes))) { #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags |= RX_PKTFLAG_CP; #endif call->app.nFree = call->app.currentPacket->length; call->app.curvec = 1; /* 0th vec is always header */ /* begin at the beginning [ more or less ], continue * on until the end, then stop. */ call->app.curpos = (char *) call->app.currentPacket->wirevec[1].iov_base + call->conn->securityHeaderSize; call->app.curlen = call->app.currentPacket->wirevec[1].iov_len - call->conn->securityHeaderSize; } if (call->error) { call->app.mode = RX_MODE_ERROR; if (call->app.currentPacket) { #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(call->app.currentPacket); call->app.currentPacket = NULL; } MUTEX_EXIT(&call->lock); return 0; } MUTEX_EXIT(&call->lock); } if (call->app.currentPacket && (int)call->app.nFree < nbytes) { /* Try to extend the current buffer */ int len, mud; len = call->app.currentPacket->length; mud = rx_MaxUserDataSize(call); if (mud > len) { int want; want = MIN(nbytes - (int)call->app.nFree, mud - len); rxi_AllocDataBuf(call->app.currentPacket, want, RX_PACKET_CLASS_SEND_CBUF); if (call->app.currentPacket->length > (unsigned)mud) call->app.currentPacket->length = mud; call->app.nFree += (call->app.currentPacket->length - len); } } /* If the remaining bytes fit in the buffer, then store them * and return. Don't ship a buffer that's full immediately to * the peer--we don't know if it's the last buffer yet */ if (!call->app.currentPacket) { call->app.nFree = 0; } while (nbytes && call->app.nFree) { t = MIN((int)call->app.curlen, nbytes); t = MIN((int)call->app.nFree, t); memcpy(call->app.curpos, buf, t); buf += t; nbytes -= t; call->app.curpos += t; call->app.curlen -= (u_short)t; call->app.nFree -= (u_short)t; if (!call->app.curlen) { /* need to get another struct iov */ if (++call->app.curvec >= call->app.currentPacket->niovecs) { /* current packet is full, extend or send it */ call->app.nFree = 0; } else { call->app.curpos = call->app.currentPacket->wirevec[call->app.curvec].iov_base; call->app.curlen = call->app.currentPacket->wirevec[call->app.curvec].iov_len; } } } /* while bytes to send and room to send them */ /* might be out of space now */ if (!nbytes) { return requestCount; } else; /* more data to send, so get another packet and keep going */ } while (nbytes); return requestCount - nbytes; }
/* rxi_ReadvProc -- internal version. * * Fills in an iovec with pointers to the packet buffers. All packets * except the last packet (new current packet) are moved to the iovq * while the application is processing the data. * * LOCKS USED -- called at netpri. */ int rxi_ReadvProc(struct rx_call *call, struct iovec *iov, int *nio, int maxio, int nbytes) { int bytes; /* Free any packets from the last call to ReadvProc/WritevProc */ if (!opr_queue_IsEmpty(&call->app.iovq)) { #ifdef RXDEBUG_PACKET call->iovqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &call->app.iovq); } if (call->app.mode == RX_MODE_SENDING) { rxi_FlushWrite(call); } MUTEX_ENTER(&call->lock); if (call->error) goto error; /* Get whatever data is currently available in the receive queue. * If rxi_FillReadVec sends an ack packet then it is possible * that we will receive more data while we drop the call lock * to send the packet. Set the RX_CALL_IOVEC_WAIT flag * here to avoid a race with the receive thread if we send * hard acks in rxi_FillReadVec. */ call->flags |= RX_CALL_IOVEC_WAIT; call->iovNBytes = nbytes; call->iovMax = maxio; call->iovNext = 0; call->iov = iov; rxi_FillReadVec(call, 0); /* if we need more data then sleep until the receive thread has * filled in the rest. */ if (!call->error && call->iovNBytes && call->iovNext < call->iovMax && !(call->flags & RX_CALL_RECEIVE_DONE)) { 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 } call->startWait = 0; } call->flags &= ~RX_CALL_IOVEC_WAIT; if (call->error) goto error; call->iov = NULL; *nio = call->iovNext; bytes = nbytes - call->iovNBytes; MUTEX_EXIT(&call->lock); return bytes; error: MUTEX_EXIT(&call->lock); call->app.mode = RX_MODE_ERROR; return 0; }
/* rxi_ReadProc -- internal version. * * LOCKS USED -- called at netpri */ int rxi_ReadProc(struct rx_call *call, char *buf, int nbytes) { int requestCount; int code; 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 (!opr_queue_IsEmpty(&call->app.iovq)) { #ifdef RXDEBUG_PACKET call->iovqc -= #endif /* RXDEBUG_PACKET */ rxi_FreePackets(0, &call->app.iovq); } do { if (call->app.nLeft == 0) { /* Get next packet */ MUTEX_ENTER(&call->lock); for (;;) { if (call->error || (call->app.mode != RX_MODE_RECEIVING)) { if (call->error) { call->app.mode = RX_MODE_ERROR; MUTEX_EXIT(&call->lock); return 0; } if (call->app.mode == RX_MODE_SENDING) { MUTEX_EXIT(&call->lock); rxi_FlushWrite(call); MUTEX_ENTER(&call->lock); continue; } } code = rxi_GetNextPacket(call); if (code) return 0; if (call->app.currentPacket) { if (!(call->flags & RX_CALL_RECEIVE_DONE)) { if (call->nHardAcks > (u_short) rxi_HardAckRate) { rxi_CancelDelayedAckEvent(call); rxi_SendAck(call, 0, 0, RX_ACK_DELAY, 0); } else { /* Delay to consolidate ack packets */ rxi_PostDelayedAckEvent(call, &rx_hardAckDelay); } } 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 } 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->app.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->app.nLeft will be smaller than the final buffer. */ while (nbytes && call->app.currentPacket) { t = MIN((int)call->app.curlen, nbytes); t = MIN(t, (int)call->app.nLeft); memcpy(buf, call->app.curpos, t); buf += t; nbytes -= t; call->app.curpos += t; call->app.curlen -= t; call->app.nLeft -= t; if (!call->app.nLeft) { /* out of packet. Get another one. */ #ifdef RX_TRACK_PACKETS call->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(call->app.currentPacket); call->app.currentPacket = NULL; } else if (!call->app.curlen) { /* need to get another struct iov */ if (++call->app.curvec >= call->app.currentPacket->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->app.currentPacket->flags &= ~RX_PKTFLAG_CP; #endif rxi_FreePacket(call->app.currentPacket); call->app.currentPacket = NULL; call->app.nLeft = 0; } else { call->app.curpos = call->app.currentPacket->wirevec[call->app.curvec].iov_base; call->app.curlen = call->app.currentPacket->wirevec[call->app.curvec].iov_len; } } } if (!nbytes) { /* user buffer is full, return */ return requestCount; } } while (nbytes); return requestCount; }
static int readHPSSconf() { int i, j, cos, code = ENOENT; afs_uint64 value; struct stat64 tstat; char tbuffer[256]; char minstr[128]; char maxstr[128]; char tmpstr[128]; static time_t lastVersion = 0; if (!initialized) { MUTEX_INIT(&rxosd_hpss_mutex, "rxosd hpss lock", 0, 0); memset(&info, 0, sizeof(info)); initialized = 1; } sprintf(tbuffer, "%s/HPSS.conf", AFSDIR_SERVER_BIN_DIRPATH); if (stat64(tbuffer, &tstat) == 0) { code = 0; #ifdef AFS_AIX53_ENV if (tstat.st_mtime > lastVersion) { #else if (tstat.st_mtim.tv_sec > lastVersion) { #endif bufio_p bp = BufioOpen(tbuffer, O_RDONLY, 0); if (bp) { while (1) { j = BufioGets(bp, tbuffer, sizeof(tbuffer)); if (j < 0) break; j = sscanf(tbuffer, "COS %u min %s max %s", &cos, &minstr, &maxstr); if (j == 3) { for (i=0; i<MAXCOS; i++) { if (cos == info[i].cosId) break; if (info[i].cosId == 0) break; } if (i<MAXCOS) code = fillInfo(&info[i], cos, minstr, maxstr); } else { j = sscanf(tbuffer, "PRINCIPAL %s", &tmpstr); if (j == 1) { strncpy(ourPrincipal, tmpstr, sizeof(ourPrincipal)); ourPrincipal[sizeof(ourPrincipal) -1] = 0; /*just in case */ continue; } j = sscanf(tbuffer, "KEYTAB %s", &tmpstr); if (j == 1) { strncpy(ourKeytab, tmpstr, sizeof(ourKeytab)); ourKeytab[sizeof(ourKeytab) -1] = 0; /*just in case */ continue; } j = sscanf(tbuffer, "PATH %s", &tmpstr); if (j == 1) { strncpy(ourPath, tmpstr, sizeof(ourPath)); ourPath[sizeof(ourPath) -1] = 0; /*just in case */ continue; } j = sscanf(tbuffer, "LIB %s", &tmpstr); if (j == 1) { int k; for (k=0; k<MAX_HPSS_LIBS; k++) { if (parms.ourLibs[k] == NULL) break; if (strcmp(parms.ourLibs[k], tmpstr) == 0) goto found; } for (k=0; k<MAX_HPSS_LIBS; k++) { if (parms.ourLibs[k] == NULL) { parms.ourLibs[k] = malloc(strlen(tmpstr) + 1); sprintf(parms.ourLibs[k], "%s", tmpstr); break; } } found: continue; } } } BufioClose(bp); } if (!code) #ifdef AFS_AIX53_ENV lastVersion = tstat.st_mtime; #else lastVersion = tstat.st_mtim.tv_sec; #endif } } return code; } static void checkCode(afs_int32 code) { /* * If we get a code of -13 back from HPSS something is wrong with our * authentication. Try to force e new authentication. */ if (code == -13) /* permission */ *(rxosd_var->lastAuth) = 0; } /* * This routine is called by the FiveMinuteCcheck */ afs_int32 authenticate_for_hpss(void) { afs_int32 code = 0, i; time_t now = time(0); static int authenticated = 0; char *principal; char *keytab; code = readHPSSconf(); if (code) return code; if (now - *(rxosd_var->lastAuth) > TWENTYDAYS) { if (authenticated) { waiting = 1; while (HPSStransactions > 0) { CV_WAIT(&auth_cond, &rxosd_hpss_mutex); } hpss_ClientAPIReset(); hpss_PurgeLoginCred(); authenticated = 0; } principal = &ourPrincipal; keytab = &ourKeytab; code = hpss_SetLoginCred(principal, hpss_authn_mech_krb5, hpss_rpc_cred_client, hpss_rpc_auth_type_keytab, keytab); if (!code) { authenticated = 1; *(rxosd_var->lastAuth) = now; } waiting = 0; if (waiters) assert(pthread_cond_broadcast(&auth_cond) == 0); } return code; }
/* 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; }