VFS: bugfixes for handling block-special files:

- on driver restarts, reopen devices on a per-file basis, not per-mount
- do not assume that there is just one vnode per block-special device
- update block-special files in the uncommon mounting success paths, too
- upon mount, sync but also invalidate affected buffers on the root FS
- upon unmount, check whether a vnode is in use before updating it
This commit is contained in:
David van Moolenbroek 2011-03-25 10:56:43 +00:00
parent 14e641cb8c
commit 28f2a169da
3 changed files with 70 additions and 40 deletions

View File

@ -20,6 +20,7 @@
#include "fs.h"
#include <fcntl.h>
#include <assert.h>
#include <sys/stat.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/endpoint.h>
@ -851,36 +852,59 @@ PUBLIC void dev_up(int maj)
* checks if any filesystems are mounted on it, and if so,
* dev_open()s them so the filesystem can be reused.
*/
int r, new_driver_e, needs_reopen, fd_nr;
int r, new_driver_e, needs_reopen, fd_nr, found;
struct filp *fp;
struct vmnt *vmp;
struct fproc *rfp;
struct vnode *vp;
/* Open a device once for every filp that's opened on it,
* and once for every filesystem mounted from it.
/* First deal with block devices. We need to consider both mounted file
* systems and open block-special files.
*/
new_driver_e = dmap[maj].dmap_driver;
/* Tell each affected mounted file system about the new endpoint. This code
* is currently useless, as driver endpoints do not change across restarts.
*/
for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) {
int minor;
if (vmp->m_dev == NO_DEV) continue;
if ( ((vmp->m_dev >> MAJOR) & BYTE) != maj) continue;
minor = ((vmp->m_dev >> MINOR) & BYTE);
if (major(vmp->m_dev) != maj) continue;
if ((r = dev_open(vmp->m_dev, VFS_PROC_NR,
vmp->m_flags ? R_BIT : (R_BIT|W_BIT))) != OK) {
printf("VFS: mounted dev %d/%d re-open failed: %d.\n",
maj, minor, r);
}
/* Send new driver endpoint */
/* Send the new driver endpoint to the mounted file system. */
if (OK != req_newdriver(vmp->m_fs_e, vmp->m_dev, new_driver_e))
printf("VFSdev_up: error sending new driver endpoint."
" FS_e: %d req_nr: %d\n", vmp->m_fs_e, REQ_NEW_DRIVER);
}
/* Look for processes that are suspened in an OPEN call. Set SUSP_REOPEN
/* For each block-special file that was previously opened on the affected
* device, we need to reopen it on the new driver.
*/
found = 0;
for (fp = filp; fp < &filp[NR_FILPS]; fp++) {
if(fp->filp_count < 1 || !(vp = fp->filp_vno)) continue;
if(major(vp->v_sdev) != maj) continue;
if(!S_ISBLK(vp->v_mode)) continue;
/* Reopen the device on the driver, once per filp. */
if ((r = dev_open(vp->v_sdev, VFS_PROC_NR, fp->filp_mode)) != OK)
printf("VFS: mounted dev %d/%d re-open failed: %d.\n",
maj, minor(vp->v_sdev), r);
found = 1;
}
/* If any block-special file was open for this major at all, also inform the
* root file system about the new endpoint of the driver. We do this even if
* the block-special file is linked to another mounted file system, merely
* because it is more work to check for that case.
*/
if (found) {
if (OK != req_newdriver(ROOT_FS_E, makedev(maj, 0), new_driver_e))
printf("VFSdev_up: error sending new driver endpoint."
" FS_e: %d req_nr: %d\n", ROOT_FS_E, REQ_NEW_DRIVER);
}
/* The rest of the code deals with character-special files. To start with,
* look for processes that are suspened in an OPEN call. Set SUSP_REOPEN
* to indicate that this process was suspended before the call to dev_up.
*/
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
@ -901,11 +925,9 @@ PUBLIC void dev_up(int maj)
needs_reopen= FALSE;
for (fp = filp; fp < &filp[NR_FILPS]; fp++) {
struct vnode *vp;
if(fp->filp_count < 1 || !(vp = fp->filp_vno)) continue;
if(((vp->v_sdev >> MAJOR) & BYTE) != maj) continue;
if(!(vp->v_mode & (I_BLOCK_SPECIAL|I_CHAR_SPECIAL))) continue;
if(!S_ISCHR(vp->v_mode)) continue;
fp->filp_state = FS_NEEDS_REOPEN;
needs_reopen = TRUE;

View File

@ -440,7 +440,6 @@ PRIVATE void init_root()
struct vnode *root_node;
struct dmap *dp;
char *label;
message m;
struct node_details res;
/* Open the root device. */

View File

@ -41,7 +41,23 @@ PRIVATE bitchunk_t nonedev[BITMAP_CHUNKS(NR_NONEDEVS)] = { 0 };
FORWARD _PROTOTYPE( dev_t name_to_dev, (int allow_mountpt) );
FORWARD _PROTOTYPE( int mount_fs, (endpoint_t fs_e) );
FORWARD _PROTOTYPE( int is_nonedev, (dev_t dev) );
FORWARD _PROTOTYPE( dev_t find_free_nonedev, (void) );
FORWARD _PROTOTYPE( dev_t find_free_nonedev, (void) );
/*===========================================================================*
* update_bspec *
*===========================================================================*/
PRIVATE void update_bspec(dev_t dev, endpoint_t fs_e)
{
/* Update all block special files for a certain device, to use a new FS endpt
* to route raw block I/O requests through.
*/
struct vnode *vp;
for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; ++vp)
if (vp->v_ref_count > 0 && S_ISBLK(vp->v_mode) && vp->v_sdev == dev)
vp->v_bfs_e = fs_e;
}
/*===========================================================================*
* do_fslogin *
@ -136,19 +152,11 @@ PRIVATE int mount_fs(endpoint_t fs_e)
* same device (partition) */
for (bspec = &vnode[0]; bspec < &vnode[NR_VNODES]; ++bspec) {
if (bspec->v_ref_count > 0 && bspec->v_sdev == dev) {
/* Found, sync the buffer cache */
req_sync(bspec->v_fs_e);
/* Found, flush and invalidate any blocks for this device. */
req_flush(bspec->v_fs_e, dev);
break;
/* Note: there are probably some blocks in the FS process'
* buffer cache which contain data on this minor, although
* they will be purged since the handling moves to the new
* FS process (if everything goes well with the mount...)
*/
}
}
/* Didn't find? */
if (bspec == &vnode[NR_VNODES] && bspec->v_sdev != dev)
bspec = NULL;
/* Scan vmnt table to see if dev already mounted. If not, find a free slot.*/
found = FALSE;
@ -202,6 +210,7 @@ PRIVATE int mount_fs(endpoint_t fs_e)
strcpy(vmp->m_label, mount_label);
allow_newroot = 0; /* The root is now fixed */
if (nodev) alloc_nonedev(dev); /* Make the allocation final */
update_bspec(dev, fs_e); /* Update open block-special files */
return(OK);
} else if (vmp == NULL) {
@ -218,11 +227,10 @@ PRIVATE int mount_fs(endpoint_t fs_e)
/* Get vnode of mountpoint */
if ((vp = eat_path(PATH_NOFLAGS, fp)) == NULL) return(err_code);
if (vp->v_ref_count != 1) {
put_vnode(vp);
return(EBUSY);
}
if (vp->v_ref_count != 1) {
put_vnode(vp);
return(EBUSY);
}
/* Tell FS on which vnode it is mounted (glue into mount tree) */
if ((r = req_mountpoint(vp->v_fs_e, vp->v_inode_nr)) != OK) {
@ -285,6 +293,7 @@ PRIVATE int mount_fs(endpoint_t fs_e)
vmp->m_mounted_on = NULL;
strcpy(vmp->m_label, mount_label);
if (nodev) alloc_nonedev(dev);
update_bspec(dev, fs_e);
root_dev = dev;
ROOT_FS_E = fs_e;
@ -331,9 +340,8 @@ PRIVATE int mount_fs(endpoint_t fs_e)
/* Allocate the pseudo device that was found, if not using a real device. */
if (nodev) alloc_nonedev(dev);
/* There was a block spec file open, and it should be handled by the
* new FS proc now */
if (bspec) bspec->v_bfs_e = fs_e;
/* The new FS will handle block I/O requests for its device now. */
update_bspec(dev, fs_e);
return(OK);
}
@ -431,8 +439,9 @@ PUBLIC int unmount(
/* Is there a block special file that was handled by that partition? */
for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; vp++) {
if((vp->v_mode & I_TYPE)==I_BLOCK_SPECIAL && vp->v_bfs_e==vmp->m_fs_e){
if(vp->v_ref_count > 0 && S_ISBLK(vp->v_mode) &&
vp->v_bfs_e == vmp->m_fs_e) {
/* Get the driver endpoint of the block spec device */
dp = &dmap[(dev >> MAJOR) & BYTE];
if (dp->dmap_driver == NONE) {