#include "inc.h"
#include "../mfs/const.h"
+#include "../vfs/const.h"
#include "../vfs/fproc.h"
#include "../vfs/dmap.h"
#include <minix/dmap.h>
fp = &fproc[i];
if (fp->fp_pid <= 0) continue;
if (++n > 22) break;
- printf("%3d %4d %2d/%d 0x%05x %2d (%d) %2d (%d) %3d %3d %3d %4d\n",
+ printf("%3d %4d %2d/%d 0x%05x %2d (%d) %2d (%d) %3d %3d %3d ",
i, fp->fp_pid,
((fp->fp_tty>>MAJOR)&BYTE), ((fp->fp_tty>>MINOR)&BYTE),
fp->fp_umask,
fp->fp_realuid, fp->fp_effuid, fp->fp_realgid, fp->fp_effgid,
fp->fp_sesldr,
- fp->fp_suspended, fp->fp_revived, fp->fp_task
+ fp->fp_blocked_on, fp->fp_revived
);
+ if (fp->fp_blocked_on == FP_BLOCKED_ON_OTHER)
+ printf("%4d\n", fp->fp_task);
+ else
+ printf(" nil\n");
}
if (i >= NR_PROCS) i = 0;
else printf("--more--\r");
#define SYS_UID ((uid_t) 0) /* uid_t for processes MM and INIT */
#define SYS_GID ((gid_t) 0) /* gid_t for processes MM and INIT */
-#define XPIPE (-NR_TASKS-1) /* used in fp_task when susp'd on pipe */
-#define XLOCK (-NR_TASKS-2) /* used in fp_task when susp'd on lock */
-#define XPOPEN (-NR_TASKS-3) /* used in fp_task when susp'd on pipe open */
-#define XSELECT (-NR_TASKS-4) /* used in fp_task when susp'd on select */
-#define XDOPEN (-NR_TASKS-5) /* used in fp_task when susp'd on device open */
+#define FP_BLOCKED_ON_NONE 0 /* not blocked */
+#define FP_BLOCKED_ON_PIPE 1 /* susp'd on pipe */
+#define FP_BLOCKED_ON_LOCK 2 /* susp'd on lock */
+#define FP_BLOCKED_ON_POPEN 3 /* susp'd on pipe open */
+#define FP_BLOCKED_ON_SELECT 4 /* susp'd on select */
+#define FP_BLOCKED_ON_DOPEN 5 /* susp'd on device open */
+#define FP_BLOCKED_ON_OTHER 6 /* blocked on other process, check
+ fp_task to find out */
+
+/* test if the process is blocked on something */
+#define fp_is_blocked(fp) ((fp)->fp_blocked_on != FP_BLOCKED_ON_NONE)
#define DUP_MASK 0100 /* mask to distinguish dup2 from dup */
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if(rfp->fp_pid == PID_FREE)
continue;
- if(rfp->fp_suspended == SUSPENDED &&
- rfp->fp_task == -driver && rfp->fp_grant == g) {
+ if(rfp->fp_blocked_on == FP_BLOCKED_ON_OTHER &&
+ rfp->fp_task == driver && rfp->fp_grant == g) {
return rfp->fp_endpoint;
}
}
/* Suspend user. */
fp->fp_grant = GRANT_INVALID;
fp->fp_ioproc = NONE;
- suspend(dp->dmap_driver);
+ wait_for(dp->dmap_driver);
fp->fp_flags |= SUSP_REOPEN;
return(SUSPEND);
}
/* Convert DEV_* to DEV_*_S variants. */
buf_used = buf;
safe = safe_io_conversion(dp->dmap_driver, &gid,
- &op, gids, NR_IOREQS, &dev_mess.IO_ENDPT, &buf_used,
+ &op, gids, NR_IOREQS, (endpoint_t*) &dev_mess.IO_ENDPT, &buf_used,
&vec_grants, bytes, &pos_lo);
if(buf != buf_used)
/* select() will do suspending itself. */
if(op != DEV_SELECT) {
/* Suspend user. */
- suspend(dp->dmap_driver);
+ wait_for(dp->dmap_driver);
}
assert(!GRANT_VALID(fp->fp_grant));
fp->fp_grant = gid; /* revoke this when unsuspended. */
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if(rfp->fp_pid == PID_FREE)
continue;
- if(rfp->fp_suspended != SUSPENDED || rfp->fp_task != -XDOPEN)
+ if(rfp->fp_blocked_on != FP_BLOCKED_ON_DOPEN)
continue;
- printf("dev_up: found process in XDOPEN, fd %d\n",
+ printf("dev_up: found process in FP_BLOCKED_ON_DOPEN, fd %d\n",
rfp->fp_fd >> 8);
fd_nr= (rfp->fp_fd >> 8);
fp= rfp->fp_filp[fd_nr];
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if(rfp->fp_pid == PID_FREE)
continue;
- if(rfp->fp_suspended == SUSPENDED &&
- rfp->fp_task == -driver_e &&
+ if(rfp->fp_blocked_on == FP_BLOCKED_ON_OTHER &&
+ rfp->fp_task == driver_e &&
(rfp->fp_flags & SUSP_REOPEN))
{
rfp->fp_flags &= ~SUSP_REOPEN;
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
reply(rfp->fp_endpoint, ERESTART);
}
}
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if (rfp->fp_pid == PID_FREE)
continue;
- if (rfp->fp_suspended != SUSPENDED ||
- rfp->fp_task != -XDOPEN ||
+ if (rfp->fp_blocked_on == FP_BLOCKED_ON_DOPEN ||
!(rfp->fp_flags & SUSP_REOPEN))
- {
continue;
- }
- printf("restart_reopen: found process in XDOPEN, fd %d\n",
+ printf("restart_reopen: found process in FP_BLOCKED_ON_DOPEN, fd %d\n",
rfp->fp_fd >> 8);
fd_nr= (rfp->fp_fd >> 8);
fp= rfp->fp_filp[fd_nr];
if (!fp)
{
/* Open failed, and automatic reopen was not requested */
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
FD_CLR(fd_nr, &rfp->fp_filp_inuse);
reply(rfp->fp_endpoint, EIO);
continue;
if ((vp->v_mode & I_TYPE) != I_CHAR_SPECIAL) continue;
if (((vp->v_sdev >> MAJOR) & BYTE) != maj) continue;
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
reply(rfp->fp_endpoint, fd_nr);
}
}
/*===========================================================================*
* dmap_driver_match *
*===========================================================================*/
-PUBLIC int dmap_driver_match(int proc, int major)
+PUBLIC int dmap_driver_match(endpoint_t proc, int major)
{
if (major < 0 || major >= NR_DEVICES) return(0);
if(dmap[major].dmap_driver != NONE && dmap[major].dmap_driver == proc)
+#ifndef __VFS_FPROC_H__
+#define __VFS_FPROC_H__
+
#include <sys/select.h>
#include <minix/safecopies.h>
char *fp_buffer; /* place to save buffer if rd/wr can't finish*/
int fp_nbytes; /* place to save bytes if rd/wr can't finish */
int fp_cum_io_partial; /* partial byte count if rd/wr can't finish */
- int fp_suspended; /* set to indicate process hanging */
int fp_revived; /* set to indicate process being revived */
- int fp_task; /* which task is proc suspended on */
+ endpoint_t fp_task; /* which task is proc suspended on */
+ int fp_blocked_on; /* what is it blocked on */
endpoint_t fp_ioproc; /* proc no. in suspended-on i/o message */
cp_grant_id_t fp_grant; /* revoke this grant on unsuspend if > -1 */
*/
/* Field values. */
-/* fp_suspended is one of these. */
-#define NOT_SUSPENDED 0xC0FFEE /* process is not suspended on pipe or task */
-#define SUSPENDED 0xDEAD /* process is suspended on pipe or task */
-
#define NOT_REVIVING 0xC0FFEEE /* process is not being revived */
#define REVIVING 0xDEEAD /* process is being revived from suspension */
#define PID_FREE 0 /* process slot free */
-/* Check is process number is acceptable - includes system processes. */
-#define isokprocnr(n) ((unsigned)((n)+NR_TASKS) < NR_PROCS + NR_TASKS)
-
+#endif /* __VFS_FPROC_H__ */
return(EAGAIN);
} else {
/* For F_SETLKW, suspend the process. */
- suspend(XLOCK);
+ suspend(FP_BLOCKED_ON_LOCK);
return(SUSPEND);
}
}
* locking).
*/
- int task;
struct fproc *fptr;
for (fptr = &fproc[INIT_PROC_NR + 1]; fptr < &fproc[NR_PROCS]; fptr++){
if(fptr->fp_pid == PID_FREE) continue;
- task = -fptr->fp_task;
- if (fptr->fp_suspended == SUSPENDED && task == XLOCK) {
+ if (fptr->fp_blocked_on == FP_BLOCKED_ON_LOCK) {
revive(fptr->fp_endpoint, 0);
}
}
/*===========================================================================*
* main *
*===========================================================================*/
-PUBLIC int main()
+PUBLIC int main(void)
{
/* This is the main program of the file system. The main loop consists of
* three major activities: getting new work, processing the work, and sending
super_user = (fp->fp_effuid == SU_UID ? TRUE : FALSE); /* su? */
#if DO_SANITYCHECKS
- if(fp->fp_suspended != NOT_SUSPENDED) {
- printf("VFS: requester %d call %d: not not suspended\n",
+ if(fp_is_blocked(fp)) {
+ printf("VFS: requester %d call %d: suspended\n",
who_e, call_nr);
panic(__FILE__, "requester suspended", NO_NUM);
}
m_in.fd = (rp->fp_fd >>8) & BYTE;
m_in.buffer = rp->fp_buffer;
m_in.nbytes = rp->fp_nbytes;
- rp->fp_suspended = NOT_SUSPENDED; /*no longer hanging*/
+ /*no longer hanging*/
+ rp->fp_blocked_on = FP_BLOCKED_ON_NONE;
rp->fp_revived = NOT_REVIVING;
reviving--;
/* This should be a pipe I/O, not a device I/O.
*/
assert(!GRANT_VALID(rp->fp_grant));
- if (rp->fp_task == -XPIPE)
+ if (rp->fp_blocked_on == FP_BLOCKED_ON_PIPE)
{
fp= rp;
fd_nr= (rp->fp_fd >> 8);
who_e = m_in.m_source;
who_p = _ENDPOINT_P(who_e);
- if(who_p < -NR_TASKS || who_p >= NR_PROCS)
+ /*
+ * negative who_p is never used to access the fproc array. Negative numbers
+ * (kernel tasks) are treated in a special way
+ */
+ if(who_p >= (int)(sizeof(fproc) / sizeof(struct fproc)))
panic(__FILE__,"receive process out of range", who_p);
if(who_p >= 0 && fproc[who_p].fp_endpoint == NONE) {
printf("FS: ignoring request from %d, endpointless slot %d (%d)\n",
rfp->fp_effgid = (gid_t) SYS_GID;
rfp->fp_umask = ~0;
rfp->fp_grant = GRANT_INVALID;
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
rfp->fp_revived = NOT_REVIVING;
} while (TRUE); /* continue until process NONE */
PRIVATE void service_pm()
{
int r, call;
- struct vmnt *vmp;
message m;
/* Ask PM for work until there is nothing left to do */
*===========================================================================*/
PRIVATE void free_proc(struct fproc *exiter, int flags)
{
- int i, task;
+ int i;
register struct fproc *rfp;
register struct filp *rfilp;
register struct vnode *vp;
panic(__FILE__, "free_proc: already free", NO_NUM);
}
- if (fp->fp_suspended == SUSPENDED) {
+ if (fp_is_blocked(fp)) {
SANITYCHECK;
- task = -fp->fp_task;
unpause(fp->fp_endpoint);
SANITYCHECK;
}
/* Invoke the driver for special processing. */
r = dev_open(vp->v_sdev, who_e, bits | (oflags & ~O_ACCMODE));
if (r == SUSPEND)
- suspend(XDOPEN); /* suspend caller */
+ suspend(FP_BLOCKED_ON_DOPEN); /* suspend caller */
break;
case I_BLOCK_SPECIAL:
if (oflags & O_NONBLOCK) {
if (bits & W_BIT) return(ENXIO);
} else {
- suspend(XPOPEN); /* suspend caller */
+ suspend(FP_BLOCKED_ON_POPEN); /* suspend caller */
return(SUSPEND);
}
} else if (susp_count > 0) {/* revive blocked processes */
/*===========================================================================*
* suspend *
*===========================================================================*/
-PUBLIC void suspend(task)
-int task; /* who is proc waiting for? (PIPE = pipe) */
+PUBLIC void suspend(int why)
{
/* Take measures to suspend the processing of the present system call.
* Store the parameters to be used upon resuming in the process table.
*/
#if DO_SANITYCHECKS
- if (task == XPIPE)
- panic(__FILE__, "suspend: called for XPIPE", NO_NUM);
+ if (why == FP_BLOCKED_ON_PIPE)
+ panic(__FILE__, "suspend: called for FP_BLOCKED_ON_PIPE", NO_NUM);
- if(fp->fp_suspended == SUSPENDED)
+ if(fp_is_blocked(fp))
panic(__FILE__, "suspend: called for suspended process", NO_NUM);
+
+ if(why == FP_BLOCKED_ON_NONE)
+ panic(__FILE__, "suspend: called for FP_BLOCKED_ON_NONE", NO_NUM);
#endif
- if (task == XPOPEN) susp_count++;/* #procs susp'ed on pipe*/
- fp->fp_suspended = SUSPENDED;
+ if (why == FP_BLOCKED_ON_POPEN)
+ /* #procs susp'ed on pipe*/
+ susp_count++;
+
+ fp->fp_blocked_on = why;
assert(fp->fp_grant == GRANT_INVALID || !GRANT_VALID(fp->fp_grant));
fp->fp_fd = m_in.fd << 8 | call_nr;
- if(task == NONE)
- panic(__FILE__,"suspend on NONE",NO_NUM);
- fp->fp_task = -task;
fp->fp_flags &= ~SUSP_REOPEN; /* Clear this flag. The caller
* can set it when needed.
*/
- if (task == XLOCK) {
+ if (why == FP_BLOCKED_ON_LOCK) {
fp->fp_buffer = (char *) m_in.name1; /* third arg to fcntl() */
fp->fp_nbytes = m_in.request; /* second arg to fcntl() */
} else {
}
}
+PUBLIC void wait_for(endpoint_t who)
+{
+ if(who == NONE || who == ANY)
+ panic(__FILE__,"suspend on NONE or ANY",NO_NUM);
+ suspend(FP_BLOCKED_ON_OTHER);
+ fp->fp_task = who;
+}
+
/*===========================================================================*
* pipe_suspend *
*===========================================================================*/
* The SUSPEND pseudo error should be returned after calling suspend().
*/
#if DO_SANITYCHECKS
- if(fp->fp_suspended == SUSPENDED)
+ if(fp_is_blocked(fp))
panic(__FILE__, "pipe_suspend: called for suspended process", NO_NUM);
#endif
susp_count++; /* #procs susp'ed on pipe*/
- fp->fp_suspended = SUSPENDED;
+ fp->fp_blocked_on = FP_BLOCKED_ON_PIPE;
assert(!GRANT_VALID(fp->fp_grant));
fp->fp_fd = (fd_nr << 8) | ((rw_flag == READING) ? READ : WRITE);
- fp->fp_task = -XPIPE;
fp->fp_buffer = buf;
fp->fp_nbytes = size;
}
/*===========================================================================*
* unsuspend_by_endpt *
*===========================================================================*/
-PUBLIC void unsuspend_by_endpt(int proc_e)
+PUBLIC void unsuspend_by_endpt(endpoint_t proc_e)
{
struct fproc *rp;
int client = 0;
*/
for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++, client++)
if(rp->fp_pid != PID_FREE &&
- rp->fp_suspended == SUSPENDED && rp->fp_task == -proc_e) {
+ rp->fp_blocked_on == FP_BLOCKED_ON_OTHER && rp->fp_task == proc_e) {
revive(rp->fp_endpoint, EAGAIN);
}
/* Search the proc table. */
for (rp = &fproc[0]; rp < &fproc[NR_PROCS] && count > 0; rp++) {
- if (rp->fp_pid != PID_FREE && rp->fp_suspended == SUSPENDED &&
+ if (rp->fp_pid != PID_FREE && fp_is_blocked(rp) &&
rp->fp_revived == NOT_REVIVING &&
(rp->fp_fd & BYTE) == call_nr &&
rp->fp_filp[rp->fp_fd>>8]->filp_vno == vp) {
* is the way it is eventually released.
*/
register struct fproc *rfp;
- register int task;
+ int blocked_on;
int fd_nr, proc_nr;
struct filp *fil_ptr;
return;
rfp = &fproc[proc_nr];
- if (rfp->fp_suspended == NOT_SUSPENDED || rfp->fp_revived == REVIVING)return;
+ if (!fp_is_blocked(rfp) || rfp->fp_revived == REVIVING)
+ return;
/* The 'reviving' flag only applies to pipes. Processes waiting for TTY get
* a message right away. The revival process is different for TTY and pipes.
* For select and TTY revival, the work is already done, for pipes it is not:
* the proc must be restarted so it can try again.
*/
- task = -rfp->fp_task;
- if (task == XPIPE || task == XLOCK) {
+ blocked_on = rfp->fp_blocked_on;
+ if (blocked_on == FP_BLOCKED_ON_PIPE || blocked_on == FP_BLOCKED_ON_LOCK) {
/* Revive a process suspended on a pipe or lock. */
rfp->fp_revived = REVIVING;
reviving++; /* process was waiting on pipe or lock */
}
- else if (task == XDOPEN)
+ else if (blocked_on == FP_BLOCKED_ON_DOPEN)
{
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
fd_nr= rfp->fp_fd>>8;
if (returned < 0)
{
reply(proc_nr_e, fd_nr);
}
else {
- rfp->fp_suspended = NOT_SUSPENDED;
- if (task == XPOPEN) /* process blocked in open or create */
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
+ if (blocked_on == FP_BLOCKED_ON_POPEN)
+ /* process blocked in open or create */
reply(proc_nr_e, rfp->fp_fd>>8);
- else if (task == XSELECT) {
+ else if (blocked_on == FP_BLOCKED_ON_SELECT) {
reply(proc_nr_e, returned);
}
else {
*/
register struct fproc *rfp;
- int proc_nr_p, task, fild, status = EINTR;
+ int proc_nr_p, blocked_on, fild, status = EINTR;
struct filp *f;
dev_t dev;
message mess;
}
rfp = &fproc[proc_nr_p];
- if (rfp->fp_suspended == NOT_SUSPENDED)
+ if (!fp_is_blocked(rfp))
return;
- task = -rfp->fp_task;
+ blocked_on = rfp->fp_blocked_on;
if (rfp->fp_revived == REVIVING)
{
}
- switch (task) {
- case XPIPE: /* process trying to read or write a pipe */
+ switch (blocked_on) {
+ case FP_BLOCKED_ON_PIPE:/* process trying to read or write a pipe */
break;
- case XLOCK: /* process trying to set a lock with FCNTL */
+ case FP_BLOCKED_ON_LOCK:/* process trying to set a lock with FCNTL */
break;
- case XSELECT: /* process blocking on select() */
+ case FP_BLOCKED_ON_SELECT:/* process blocking on select() */
select_forget(proc_nr_e);
break;
- case XPOPEN: /* process trying to open a fifo */
+ case FP_BLOCKED_ON_POPEN: /* process trying to open a fifo */
break;
- case XDOPEN: /* process trying to open a device */
+ case FP_BLOCKED_ON_DOPEN:/* process trying to open a device */
/* Don't cancel OPEN. Just wait until the open completes. */
return;
- default: /* process trying to do device I/O (e.g. tty)*/
+ case FP_BLOCKED_ON_OTHER: /* process trying to do device I/O (e.g. tty)*/
if (rfp->fp_flags & SUSP_REOPEN)
{
/* Process is suspended while waiting for a reopen.
mess.COUNT = (rfp->fp_fd & BYTE) == READ ? R_BIT : W_BIT;
mess.m_type = CANCEL;
fp = rfp; /* hack - ctty_io uses fp */
- (*dmap[(dev >> MAJOR) & BYTE].dmap_io)(task, &mess);
+ (*dmap[(dev >> MAJOR) & BYTE].dmap_io)(rfp->fp_task, &mess);
status = mess.REP_STATUS;
if (status == SUSPEND)
return; /* Process will be revived at a
}
rfp->fp_grant = GRANT_INVALID;
}
+ break;
+ default :
+ panic(__FILE__,"FS: unknown value", blocked_on);
}
- rfp->fp_suspended = NOT_SUSPENDED;
+ rfp->fp_blocked_on = FP_BLOCKED_ON_NONE;
- if ((task == XPIPE || task == XPOPEN) && !wasreviving) {
+ if ((blocked_on == FP_BLOCKED_ON_PIPE ||
+ blocked_on == FP_BLOCKED_ON_POPEN) &&
+ !wasreviving) {
susp_count--;
}
for (rfp=&fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if (rfp->fp_pid == PID_FREE)
continue;
- if(rfp->fp_suspended != SUSPENDED &&
- rfp->fp_suspended != NOT_SUSPENDED) {
- printf("check_pipe: %d invalid suspended value 0x%x\n",
- rfp->fp_endpoint, rfp->fp_suspended);
- return 0;
- }
- if(rfp->fp_suspended == SUSPENDED && rfp->fp_revived != REVIVING && (-rfp->fp_task == XPIPE || -rfp->fp_task == XPOPEN)) {
+ if(rfp->fp_revived != REVIVING &&
+ (rfp->fp_blocked_on == FP_BLOCKED_ON_PIPE ||
+ rfp->fp_blocked_on == FP_BLOCKED_ON_POPEN)) {
mycount++;
}
}
_PROTOTYPE( void build_dmap, (void) );
_PROTOTYPE( int map_driver, (int major, int proc_nr, int dev_style,
int force) );
-_PROTOTYPE( int dmap_driver_match, (int proc, int major) );
+_PROTOTYPE( int dmap_driver_match, (endpoint_t proc, int major) );
_PROTOTYPE( void dmap_unmap_by_endpt, (int proc_nr) );
_PROTOTYPE( void dmap_endpt_up, (int proc_nr) );
_PROTOTYPE( int select_request_pipe, (struct filp *f, int *ops, int bl) );
_PROTOTYPE( int select_cancel_pipe, (struct filp *f) );
_PROTOTYPE( int select_match_pipe, (struct filp *f) );
-_PROTOTYPE( void unsuspend_by_endpt, (int) );
+_PROTOTYPE( void unsuspend_by_endpt, (endpoint_t) );
_PROTOTYPE( void select_reply1, (void) );
_PROTOTYPE( void select_reply2, (void) );
#if DO_SANITYCHECKS
_PROTOTYPE( void select_forget, (int fproc) );
_PROTOTYPE( void select_timeout_check, (timer_t *) );
_PROTOTYPE( void init_select, (void) );
-_PROTOTYPE( void select_unsuspend_by_endpt, (int proc) );
+_PROTOTYPE( void select_unsuspend_by_endpt, (endpoint_t proc) );
_PROTOTYPE( int select_notified, (int major, int minor, int ops) );
/* timers.c */
selecttab[s].requestor = fp;
/* process now blocked */
- suspend(XSELECT);
+ suspend(FP_BLOCKED_ON_SELECT);
return SUSPEND;
}
/*===========================================================================*
* select_unsuspend_by_endpt *
*===========================================================================*/
-PUBLIC void select_unsuspend_by_endpt(int proc_e)
+PUBLIC void select_unsuspend_by_endpt(endpoint_t proc_e)
{
int fd, s;