unit kern_ksched;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 rtprio,
 time;

const
 //POSIX scheduling policies
 SCHED_FIFO =1;
 SCHED_OTHER=2;
 SCHED_RR   =3;

type
 p_sched_param=^t_sched_param;
 t_sched_param=packed record
  sched_priority:Integer;
 end;

 p_ksched=^t_ksched;
 t_ksched=packed record
  rr_interval:timespec;
 end;

function sys_sched_setparam(pid:Integer;param:Pointer):Integer;
function sys_sched_getparam(pid:Integer;param:Pointer):Integer;
function sys_sched_setscheduler(pid,policy:Integer;param:Pointer):Integer;
function sys_sched_getscheduler(pid:Integer):Integer;
function sys_sched_get_priority_max(policy:Integer):Integer;
function sys_sched_get_priority_min(policy:Integer):Integer;
function sys_sched_rr_get_interval(pid:Integer;interval:Pointer):Integer;

implementation

uses
 errno,
 systm,
 kern_thr,
 kern_proc,
 md_proc;

const
 sched_rr_interval=4;

var
 //Configured in kernel version:
 ksched:t_ksched=(rr_interval:(tv_sec:0;tv_nsec:1000000000 div sched_rr_interval));

function ksched_get_priority_max(ksched:p_ksched;policy:DWORD;prio:PInteger):Integer;
begin
 if ((policy or 2)=3) then //SCHED_FIFO,SCHED_RR
 begin
  prio^:=PRI_MAX_TIMESHARE;
  Exit(0);
 end;
 Exit(EINVAL);
end;

function ksched_get_priority_min(ksched:p_ksched;policy:DWORD;prio:PInteger):Integer;
begin
 if ((policy or 2)=3) then //SCHED_FIFO,SCHED_RR
 begin
  prio^:=PRI_MIN_TIMESHARE;
  Exit(0);
 end;
 Exit(EINVAL);
end;

function ksched_getparam(ksched:p_ksched;td:p_kthread;param:p_sched_param):Integer;
var
 rtp:t_rtprio;
begin
 pri_to_rtp(td,@rtp);
 param^.sched_priority:=rtp._prio;
 Exit(0);
end;

function ksched_getscheduler(ksched:p_ksched;td:p_kthread;policy:PDWORD):Integer;
var
 rtp:t_rtprio;
 p:DWORD;
begin
 pri_to_rtp(td,@rtp);
 p:=SCHED_FIFO;
 if (rtp._type<>PRI_FIFO) then
 begin
  p:=ord(rtp._type=PRI_REALTIME) or 2; //SCHED_OTHER,SCHED_RR
 end;
 policy^:=p;
 Exit(0);
end;

function ksched_rr_get_interval(ksched:p_ksched;td:p_kthread;time:p_timespec):Integer;
begin
 time^:=ksched^.rr_interval;
 Exit(0);
end;

function ksched_setparam(ksched:p_ksched;td:p_kthread;param:p_sched_param):Integer;
var
 rtp:t_rtprio;
 policy:DWORD;
begin
 Result:=EPERM;
 policy:=SCHED_FIFO;
 pri_to_rtp(td,@rtp);
 if (rtp._type<>PRI_FIFO) then
 begin
  if (rtp._type<>PRI_REALTIME) then
  begin
   Exit(EINVAL);
  end;
  policy:=SCHED_RR;
 end;
 if ((param^.sched_priority - PRI_MIN_TIMESHARE) < 512) then
 begin
  rtp._prio:=param^.sched_priority;
  rtp._type:=ord(policy=SCHED_FIFO) * 8 + 2; //PRI_REALTIME,PRI_FIFO
  rtp_to_pri(@rtp,td);
  Result:=0;
 end;
end;

function ksched_setscheduler(ksched:p_ksched;td:p_kthread;policy:DWORD;param:p_sched_param):Integer;
var
 rtp:t_rtprio;
begin
 if ((policy or 2)=3) then //SCHED_FIFO,SCHED_RR
 begin
  Result:=EPERM;
  if ((param^.sched_priority - PRI_MIN_TIMESHARE) < 512) then
  begin
   rtp._prio:=param^.sched_priority;
   rtp._type:=ord(policy=SCHED_FIFO) * 8 + 2; //PRI_REALTIME,PRI_FIFO
   rtp_to_pri(@rtp,td);
   Result:=0;
  end;
 end else
 begin
  Result:=EINVAL;
 end;
end;

/////////

function sys_sched_setparam(pid:Integer;param:Pointer):Integer;
var
 td:p_kthread;
 e:Integer;
 sched_param:t_sched_param;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 e:=copyin(param, @sched_param, sizeof(sched_param));
 if (e<>0) then Exit(e);

 if (pid=0) or (pid=p_proc.p_pid) then
 begin
  //
 end else
 begin
  Exit(ESRCH);
 end;

 PROC_LOCK();
 e:=ksched_setparam(@ksched,td,@sched_param);
 PROC_UNLOCK();

 Exit(e);
end;

function sys_sched_getparam(pid:Integer;param:Pointer):Integer;
var
 td:p_kthread;
 e:Integer;
 sched_param:t_sched_param;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 if (pid=0) or (pid=p_proc.p_pid) then
 begin
  //
 end else
 begin
  Exit(ESRCH);
 end;

 PROC_LOCK();
 e:=ksched_getparam(@ksched, td, @sched_param);
 PROC_UNLOCK();

 if (e=0) then
  e:=copyout(@sched_param, param, sizeof(sched_param));

 Exit(e);
end;

function sys_sched_setscheduler(pid,policy:Integer;param:Pointer):Integer;
var
 td:p_kthread;
 e:Integer;
 sched_param:t_sched_param;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 { Don't allow non root user to set a scheduler policy. }
 //e:=priv_check(td, PRIV_SCHED_SET);
 //if (e<>0) then Exit(e);

 e:=copyin(param, @sched_param, sizeof(sched_param));
 if (e<>0) then Exit(e);

 if (pid=0) or (pid=p_proc.p_pid) then
 begin
  //
 end else
 begin
  Exit(ESRCH);
 end;

 PROC_LOCK();
 e:=ksched_setscheduler(@ksched, td, policy, @sched_param);
 PROC_UNLOCK();

 Exit(e);
end;

function sys_sched_getscheduler(pid:Integer):Integer;
var
 td:p_kthread;
 e,policy:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 if (pid=0) or (pid=p_proc.p_pid) then
 begin
  //
 end else
 begin
  Exit(ESRCH);
 end;

 PROC_LOCK();
 e:=ksched_getscheduler(@ksched, td, @policy);
 td^.td_retval[0]:=policy;
 PROC_UNLOCK();

 Exit(e);
end;

function sys_sched_get_priority_max(policy:Integer):Integer;
var
 td:p_kthread;
 error,prio:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 error:=ksched_get_priority_max(@ksched, policy, @prio);

 td^.td_retval[0]:=prio;
 Exit(error);
end;

function sys_sched_get_priority_min(policy:Integer):Integer;
var
 td:p_kthread;
 error,prio:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 error:=ksched_get_priority_min(@ksched, policy, @prio);

 td^.td_retval[0]:=prio;
 Exit(error);
end;

function kern_sched_rr_get_interval(td:p_kthread;pid:Integer;ts:p_timespec):Integer;
var
 e:Integer;
begin

 if (pid=0) or (pid=p_proc.p_pid) then
 begin
  //
 end else
 begin
  Exit(ESRCH);
 end;

 PROC_LOCK();
 e:=ksched_rr_get_interval(@ksched, td, ts);
 PROC_UNLOCK();

 Exit(e);
end;

function sys_sched_rr_get_interval(pid:Integer;interval:Pointer):Integer;
var
 td:p_kthread;
 time:timespec;
 error:Integer;
begin
 td:=curkthread;
 if (td=nil) then Exit(-1);

 error:=kern_sched_rr_get_interval(td, pid, @time);

 if (error=0) then
  error:=copyout(@time, interval, sizeof(time));

 Exit(error);
end;



end.

