TDF_NEEDRESCHED when extending pcb on x86
Denis Ustimenko
denus at ngs.ru
Mon Feb 28 22:55:37 GMT 2005
Hi, there!
I've tried s3switch utility from ports on 5.2.1 and found that
i386_set_ioperm syscall doesn't work properly. The next code illustrates
the problem. It will get SIGBUS with very high probability.
#include <sys/types.h>
#include <machine/sysarch.h>
#include <machine/cpufunc.h>
int main()
{
if ( i386_set_ioperm( 0x80, 1, 1)) {
perror("XXX");
return 1;
}
inb( 0x80);
return 0;
}
Now I have no 5.3 or CURRENT system but brief looking on code shows that
it should give the same result on them.
The problem occurs when we extend pcb and set TDF_NEEDRESCHED bit hoping
that thread will be rescheduled and new TSS will be loaded. But
sched_switch function skips cpu_switch when thread was not changed and
ltr is not executed.
So I've patched sys_machdep.c the patch is below. But two thoughts in
this situation are not clear to me:
1. May be patching of scheduler code to call cpu_switch in case of
TDF_NEEDRESCHED is more appropriate decision
2. Is it necessary to protect all new lines of i386_extend_pcb function
by sched_lock? It looks like we could move at least ltr out of
syncronized code.
*** /usr/src/sys/i386/include/segments.h.orig Mon Feb 28 23:34:30 2005
--- /usr/src/sys/i386/include/segments.h Mon Feb 28 23:34:53 2005
***************
*** 243,248 ****
--- 243,249 ----
extern struct gate_descriptor *idt;
extern union descriptor ldt[NLDT];
extern struct region_descriptor r_gdt, r_idt;
+ extern int private_tss;
void lgdt(struct region_descriptor *rdp);
void sdtossd(struct segment_descriptor *sdp,
*** /usr/src/sys/i386/i386/sys_machdep.c.orig Mon Feb 28 23:29:31 2005
--- /usr/src/sys/i386/i386/sys_machdep.c Mon Feb 28 23:33:31 2005
***************
*** 172,179 ****
mtx_lock_spin(&sched_lock);
td->td_pcb->pcb_ext = ext;
! /* switch to the new TSS after syscall completes */
! td->td_flags |= TDF_NEEDRESCHED;
mtx_unlock_spin(&sched_lock);
return 0;
--- 172,180 ----
mtx_lock_spin(&sched_lock);
td->td_pcb->pcb_ext = ext;
! private_tss |= 1 << PCPU_GET(cpuid);
! *PCPU_GET(tss_gdt) = td->td_pcb->pcb_ext->ext_tssd;
! ltr(GSEL(GPROC0_SEL, SEL_KPL));
mtx_unlock_spin(&sched_lock);
return 0;
--
Best regards
Denis
More information about the freebsd-hackers
mailing list