Date: Tue, 28 Nov 2000 19:15:19 -0500 (EST) From: Daniel Eischen <eischen@vigrid.com> To: Julian Elischer <julian@elischer.org> Cc: arch@FreeBSD.ORG Subject: Re: Thread-specific data and KSEs Message-ID: <Pine.SUN.3.91.1001128181622.9762A-100000@pcnet1.pcnet.com> In-Reply-To: <3A242FAF.313295F0@elischer.org>
next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 28 Nov 2000, Julian Elischer wrote: > Daniel Eischen wrote: > > I think we want to avoid saving and restoring FP state unless it's > > necessary. That's probably only when a fault occurs or when the > > KSE is preempted. I like the idea of having the kernel save the > > FP state in the thread state storage area (ucontext_t?) in the > > KSE mailbox thingy. > > The question is, what happens to the FPU context when you swap threads? > should each thread have it's own FPU context? Yes, it currently does now. It's hard to imagine every thread not having FPU context. A thread that is preempted needs to have it's context restored when it runs again. You seem to be forgetting that the UTS can choose another thread to run and _not_ resume the preempted thread. So the UTS chooses another thread, runs it, and then you can have another fault or preemption which needs to save FP state again. > If there is one for the KSE, might that be not enough? > especially if the KSE was pre-empted. If a thread is migrated to > another KSE, having last been pre-empted, it becomes important > that the FPU state go with it because it may have been part way > through some calculations when that KSE was stopped. And what if > the new KSE already has one that wsa stopped in thasame way? > it looks to me like you need to have one per thread. Ahh, exactly. > It's not much different fromt eh point of view of the kernel. > WHen you create a KSE you give it's mailbox. > when you schedule a thread onto the KSE you set a pointer in that > mailbox to the thread's context and state storage area. > The kernel can easily follow that link when it pre-empts the KSE > to store the General regs, the FPU regs etc. > Theoretically it might only store the regs there in a syscall > if it looks like the syscall will block. but the aim would be > to make allthreads look the same when stopped so that the UTS > can restart any one it chooses. That would be my goal also. > > Also, are we going to allow the kernel to follow links out of > > the mailbox, or are we going to limit UTS<->kernel communication > > to just this one page? I think it might be preferable to only > > communicate via the mailbox and never have the kernel attempt > > to read/write to other areas of KSE/thread storage. > > The kernel already has to follow links etc for (for example) > the readv() syscall. it's not that big a step. If you allocate > all the thread-context blocks together, the pages they are in will > be pretty hot. There are great advantages to having the KSEs > being able to follow links. For example it means that the kernel > can ALWAYS deliver a linked list of ALL completed and 'ready-to-run' > threads. It can set them up so that each one will look exactly > as if it has just returned from the syscall. If you only deal > with one structure, you have to consider what happens when you > cannot fit all returning threads into the single structure. You should only have to copy context back to the KSE for one thread. When a thread blocks in the kernel, you don't need to wait to copyout it's context. You can do it immediately. All you need to pass out when the thread is resumed in the kernel and ready to return to userland is the return value from the system call. For faults and preemptions there are no return values I'd guess. > As the kernel takes control (as the syscall or trap is entered) > it notes where the context block is and when and if it decide > it needs to save context, it knows where to put it. The UTS is > given everything on a plate, and it's almost easier to do it > this way for the kernel too. It can store this address with > the KSEC and use it without any fear of ever having a clash with > some other returning syscall (for example). I can imagine where > a syscall starts on one KSE and is completed on another. > it makes sence for the context to travel with the thread/KSEC > rather than the KSE, which may suddenly have 4 syscalls > all coming back within the same upcall. (where do you save > all that data?) Here's the way I see it. A thread blocks in the kernel, or is preempted, has a fault. "There can be only one" (name the movie!) thread running in the KSE at a time. You copyout the context to the KSE, _then_ make the upcall. The KSE upcall handler then copies the context (along with FP state if saved) to the threads context storage area. Another thread is chosen and executed. The kernel need not follow links, the KSE upcall handler can handle placing the context in the threads storage area. When a thread becomes unblocked in the kernel, the UTS already has its context. All it needs now is enough information for the return value of the system call. If the UTS has to munge with the context a bit, it can. This will let you set the 1 page used for the mailbox so that it won't be paged out and not worry about whether memory anywhere else in the UTS/thread is paged out. > > For instance, > > we could place the pointer to the thread state storage area > > in the mailbox. But that would require a copyin, and then a > > copyout to another page that might be paged out. > > Since the thread storage is part of the thread control block > that the UTS has just used to schedule the thread, it's > unlikely to be paged out. Even less so if it shares a page > with other thread control blocks > And you could always protect it with madvise(). > > In any case what you suggest above is EXACTLY what jason and I > were planning on doing. The kernel will define a structure > in /sys/i386/include/kse.h (or somewhere) called something like > struct user_process_context > which you would include in your thread control block. > it would include a link to other such blocks (so we can > return a linked list of completed or pre-empted threads (KSECs) > and a status word that says whether it is a completed syscall, or a > preempted thread, or whatever, and a cookie that the kernel > doesn't touch so you can extend it with whatever else you need. Well, if you want to take the extra step of performing a copyin and following a link, I can live with that. But, I wouldn't wait until the thread becomes unblocked to copyout its context. Just do it immediately and its much easier. Actually, you have to copyout its context immediately. Let's say a thread blocks in the kernel on a read(). Then let's say the thread is sent a signal (and sa_flags is SA_RESTART). The UTS needs the threads context (at least the stack pointer) so it can create a signal frame on top of its stack. The signal handler will run in the context of the thread while it is still blocked in the kernel. The thread will also need to use its context storage area because it may again be preempted or blocked. I've spent considerable time trying to get signal handling working correctly in our threads library, and this is about the only way that really works. Actually, there is even another problem. Suppose you have: static pthread_t tid; static jmp_buf jmpbuf; static void sighandler(int signo) { if (signo == SIGALRM && pthread_equal(pthread_self(), tid)) _longjmp(jmpbuf, 1); } my_thread(void *arg) { char buf[128]; int fd = (int)arg; int ret; tid == pthread_self(); if (_setjmp(jmpbuf) == 0) { ret = read(fd, buf, sizeof(buf)); } else { printf("Thread is exiting.\n"); pthread_exit(NULL); } } pthread_kill(tid, SIGUSR1); ... pthread_kill(tid, SIGALRM); ... This is perfectly valid. And you can also have compilers that generate builtin longjmps for exception handling. In that case, we can't even wrap longjmp/_longjmp in order to do cleanup handling. So the kernel still thinks the read() is active. All sorts of gnarly stuff can happen. I think we need a way to tell the kernel to halt any pending activities for the KSEC that was blocked before trying to deliver any signals. If the thread returns normally from the signal handler, then the KSEC can be resumed. In the case of an abnormal return/jump out of the signal handler, I don't know how we'd inform the kernel that the KSEC could be reused; the UTS doesn't know if the thread is still operating in the signal handler or has jumped out of it. It'd be nice if the UTS could retrieve the KSEC state/storage. If it could, the UTS could copy it to the signal handling frame so that a normal return from the signal handler could pass it back to the kernel. We need to work this out. > > The drawback > > of only using the mailbox is that it requires an additional copy > > by the UTS every time an upcall is made (to copy the thread state > > from the mailbox to the storage area in the thread). > > You forget that a single upcall may want to return 37 completed > syscalls or pre-empted threads. Already explained above. > Using my scheme.. there is an upcall, and bingo the UTS has ALL > the completed items at once. > It sorts them onto the runnable queues, selects it's favourite > and puts that address into it's mailbox, loads the context, > and it's off an running the next thread. -- Dan Eischen To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-arch" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.SUN.3.91.1001128181622.9762A-100000>