Skip to content

Commit

Permalink
Merge pull request #1742 from ghaerr/diskchange
Browse files Browse the repository at this point in the history
[kernel] First pass at adding media change support to kernel
  • Loading branch information
ghaerr authored Oct 6, 2023
2 parents 56d3bb7 + 69c43be commit d68c81b
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 152 deletions.
1 change: 1 addition & 0 deletions elks/arch/i86/drivers/block/blk.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ static void end_request(int uptodate)
req = CURRENT;

if (!uptodate) {
/*if (req->rq_errors >= 0)*/
printk(DEVICE_NAME ": I/O %s error dev %D lba sector %lu\n",
(req->rq_cmd == WRITE)? "write": "read",
req->rq_dev, req->rq_sector);
Expand Down
225 changes: 116 additions & 109 deletions elks/arch/i86/drivers/block/directfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#include <linuxmt/config.h>
#include <linuxmt/sched.h>
#include <linuxmt/fs.h>
#include <linuxmt/stat.h>
#include <linuxmt/kernel.h>
#include <linuxmt/timer.h>
#include <linuxmt/mm.h>
Expand Down Expand Up @@ -116,8 +117,8 @@
*/

#define USE_IMPLIED_SEEK 0 /* =1 for QEMU with 360k/AT stretch floppies (not real hw) */
#define CHECK_DISK_CHANGE 0 /* =1 to add driver media changed code */
#define CLEAR_DIR_REG 0 /* =1 to clear DIR DSKCHG when set (for media change) */
#define CHECK_DIR_REG 1 /* =1 to read and clear DIR DSKCHG when media changed */
#define CHECK_DISK_CHANGE 1 /* =1 to inform kernel of media changed */

//#define DEBUG printk
#define DEBUG(...)
Expand Down Expand Up @@ -250,11 +251,15 @@ static int probing;

/* device reference counters */
static int fd_ref[4];
static int access_count;

/* Synchronization of FDC access. */
static int fdc_busy;
static struct wait_queue fdc_wait;

/* bit vector set when media changed - causes I/O to be discarded until unset */
static unsigned int changed_floppies;

/*
* Threshold for reporting FDC errors to the console.
* Setting this to zero may flood your screen when using
Expand Down Expand Up @@ -299,6 +304,7 @@ static void floppy_shutdown(void);
static void motor_off_callback(int);
static int DFPROC floppy_register(void);
static void DFPROC floppy_deregister(void);
static void floppy_release(struct inode *inode, struct file *filp);

static void DFPROC delay_loop(int cnt)
{
Expand Down Expand Up @@ -437,60 +443,6 @@ void request_done(int uptodate)
end_request(uptodate);
}

#if CHECK_DISK_CHANGE
/*
* The check_media_change entry in struct file_operations (fs.h) is not
* part of the 'normal' setup (only BLOAT_FS), so we're ignoring it for now,
* assuming the user is smart enough to umount before media changes - or
* ready for the consequences.
*
* floppy_change is never called from an interrupt, so we can relax a bit
* here, sleep etc. Note that floppy_on tries to set current_DOR to point
* to the desired drive, but it will probably not survive the sleep if
* several floppies are used at the same time: thus the loop.
*/
static unsigned int changed_floppies = 0, fake_change = 0;

int floppy_change(struct buffer_head *bh)
{
unsigned int mask = 1 << (bh->b_dev & 0x03);

if (MAJOR(bh->b_dev) != MAJOR_NR) {
printk("floppy_change: not a floppy\n");
return 0;
}
if (fake_change & mask) {
buffer_track = -1;
fake_change &= ~mask;
/* omitting the next line breaks formatting in a horrible way ... */
changed_floppies &= ~mask;
return 1;
}
if (changed_floppies & mask) {
buffer_track = -1;
changed_floppies &= ~mask;
recalibrate = 1;
return 1;
}
if (!bh)
return 0;
if (EBH(bh)->b_dirty)
ll_rw_blk(WRITE, bh);
else {
buffer_track = -1;
mark_buffer_uptodate(bh, 0);
ll_rw_blk(READ, bh);
}
wait_on_buffer(bh);
if (changed_floppies & mask) {
changed_floppies &= ~mask;
recalibrate = 1;
return 1;
}
return 0;
}
#endif

/* The IBM PC can perform DMA operations by using the DMA chip. To use it,
* the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
* to be read from or written to, the byte count minus 1, and a read or write
Expand Down Expand Up @@ -995,13 +947,12 @@ static void floppy_shutdown(void)
DEBUG("[%u]shtdwn0x%x|%x-", (unsigned int)jiffies, current_DOR, running);
do_floppy = NULL;
printk("df%d: FDC cmd timeout\n", current_drive);
request_done(0);
recover = 1;
reset_floppy();
redo_fd_request();
}

#if CLEAR_DIR_REG
#if CHECK_DIR_REG
static void shake_done(void)
{
/* Need SENSEI to clear the interrupt */
Expand Down Expand Up @@ -1049,39 +1000,101 @@ static void shake_one(void)
output_byte(head << 2 | current_drive);
output_byte(1); /* resetting media change bit requires head movement */
}
#endif /* CHECK_DIR_REG */

#if CHECK_DISK_CHANGE
/*
* (User-provided) media information is _not_ discarded after a media change
* if the corresponding keep_data flag is non-zero. Positive values are
* decremented after each probe.
* This routine checks whether a removable media has been changed,
* invalidates all inode and buffer-cache-entries, unmounts
* any mounted filesystem and closes the driver.
* It is called from device open, mount and file read/write code.
* Because the FDC is interrupt driven and can't sleep, this routine
* must be called by a non-interrupt routine, as invalidate_buffers
* may sleep. Even without that, all accessed kernel variables would need
* protection via interrupt disabling.
*
* Since this driver is the only driver implementing media change,
* the entire routine has been moved here for simplicity.
*/
static int keep_data[4];
#endif /* CLEAR_DIR_REG */
#if DEBUG_EVENT
static int debug_changed;
void fake_disk_change(void)
{
debug("X");
debug_changed = 1;
}
#endif

static void DFPROC floppy_ready(void)
int check_disk_change(kdev_t dev)
{
DEBUG("RDY0x%x,%d,%d-", inb(FD_DIR), reset, recalibrate);
unsigned int mask;
struct super_block *s;
struct inode *inodep;

#if CLEAR_DIR_REG
/* check if disk changed since last cmd (PC/AT+) */
if (fdc_version >= FDC_TYPE_8272PC_AT && (inb(FD_DIR) & 0x80)) {
if (!dev && changed_floppies) {
dev = (changed_floppies & 1)? MKDEV(MAJOR_NR, 0): MKDEV(MAJOR_NR, 1);
} else if (MAJOR(dev) != MAJOR_NR)
return 0;
debug("C%d", dev&3);
mask = 1 << DEVICE_NR(dev);
if (!(changed_floppies & mask)) {
debug("N");
return 0;
}
debug("Y");

if (dev == ROOT_DEV) panic("Root media changed");

/* I/O is ignored but inuse, locked, or dirty inodes may not be cleared... */
s = get_super(dev);
if (s && s->s_mounted) {
do_umount(dev);
printk("VFS: Unmounting %s, media changed\n", s->s_mntonname);
/* fake up inode to enable device close */
inodep = new_inode(NULL, S_IFBLK);
inodep->i_rdev = dev;
floppy_release(inodep, NULL);
iput(inodep);
}
fsync_dev(dev);
invalidate_inodes(dev);
invalidate_buffers(dev);

changed_floppies &= ~mask;
debug_changed = 0;
printk("VFS: Disk media change completed on dev %D\n", dev);

return 1;
}
#endif /* CHECK_DISK_CHANGE */

static void DFPROC floppy_ready(void)
{
DEBUG("RDY%d,%d-", reset, recalibrate);

#if CHECK_DISK_CHANGE
changed_floppies |= 1 << current_drive;
/* check if disk changed since last cmd (PC/AT+) */
if (0
#if DEBUG_EVENT
|| (debug_changed && current_drive == 1)
#endif
buffer_track = -1;
if (keep_data[current_drive]) {
if (keep_data[current_drive] > 0)
keep_data[current_drive]--;
} else {
/* FIXME: this is nonsensical: Should assume that a medium change
* means a new medium of the same format as the prev until it fails.
*/
if (current_type[current_drive] != NULL)
printk("df%d: Disk type undefined after disk change\n", current_drive);
current_type[current_drive] = NULL;
#if CHECK_DIR_REG
|| (fdc_version >= FDC_TYPE_8272PC_AT && (inb(FD_DIR) & 0x80))
#endif
) {
/* first time through the FDC requires recalibrate in order to clear DIR,
* so just recalibrate instead of starting to discard I/O in that case.
*/
if (current_type[current_drive]) {
changed_floppies |= 1 << current_drive; /* this will discard any queued I/O */
printk("df%d: Disk media change detected, suspending I/O\n", current_drive);
current_type[current_drive] = NULL; /* comment out to keep last media format */
}

if (current_drive == buffer_drive)
buffer_track = -1;

#if CHECK_DIR_REG
if (!reset && !recalibrate) {
if (current_track && current_track != NO_TRACK)
do_floppy = shake_zero;
Expand All @@ -1091,8 +1104,11 @@ static void DFPROC floppy_ready(void)
output_byte(current_drive);
return;
}
}
#endif
redo_fd_request();
return;
}
#endif /* CHECK_DISK_CHANGE */

if (reset) {
reset_floppy();
Expand Down Expand Up @@ -1126,6 +1142,15 @@ static void DFPROC redo_fd_request(void)
seek = 0;
type = MINOR(req->rq_dev) >> MINOR_SHIFT;
drive = DEVICE_NR(req->rq_dev);

#if CHECK_DISK_CHANGE
if (changed_floppies & (1 << drive)) {
req->rq_errors = -1; /* stop error display */
request_done(0);
goto repeat;
}
#endif

if (type > 3)
floppy = &minor_types[type >> 2];
else { /* Auto-detection */
Expand Down Expand Up @@ -1235,15 +1260,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
}
return err;
#ifdef UNUSED
case FDFMTEND:
if (!suser())
return -EPERM;
clr_irq();
fake_change |= 1 << (drive & 3);
set_irq();
drive &= 3;
cmd = FDCLRPRM;
break;
case FDFMTTRK:
if (!suser())
return -EPERM;
Expand Down Expand Up @@ -1273,22 +1289,10 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
floppy_off(drive & 3);
wake_up(&format_done);
return err ? 0 : -EIO;
case FDFLUSH:
if (!permission(inode, 2))
return -EPERM;
clr_irq();
fake_change |= 1 << (drive & 3);
set_irq();
check_disk_change(inode->i_rdev);
return 0;
}
if (!suser())
return -EPERM;
switch (cmd) {
case FDCLRPRM:
current_type[drive] = NULL;
keep_data[drive] = 0;
break;
case FDSETPRM:
case FDDEFPRM:
memcpy_fromfs(user_params + drive,
Expand Down Expand Up @@ -1358,8 +1362,11 @@ static int floppy_open(struct inode *inode, struct file *filp)
drive = DEVICE_NR(inode->i_rdev);

#if CHECK_DISK_CHANGE
if (filp && filp->f_mode)
check_disk_change(inode->i_rdev);
debug_setcallback(2, fake_disk_change); /* debug trigger ^P */
if (filp && filp->f_mode) {
if (check_disk_change(inode->i_rdev))
return -ENXIO;
}
#endif

probing = 0;
Expand All @@ -1375,17 +1382,17 @@ static int floppy_open(struct inode *inode, struct file *filp)
}
}

if (fd_ref[drive] == 0) {
if (access_count == 0) {
err = floppy_register();
if (err) return err;
buffer_drive = buffer_track = -1;
}

access_count++;
fd_ref[drive]++;
inode->i_size = (sector_t)floppy->size << 9; /* NOTE: assumes sector size 512 */
open_inode = inode;
DEBUG("df%d: open dv %x, sz %lu, %s\n", drive, inode->i_rdev, inode->i_size,
floppy->name);
DEBUG("df%d: open %s, size %lu\n", drive, floppy->name, inode->i_size);

return 0;
}
Expand All @@ -1400,8 +1407,9 @@ static void floppy_release(struct inode *inode, struct file *filp)
fsync_dev(dev);
invalidate_inodes(dev);
invalidate_buffers(dev);
floppy_deregister();
}
if (--access_count == 0)
floppy_deregister();
}

static struct file_operations floppy_fops = {
Expand Down Expand Up @@ -1437,7 +1445,6 @@ static void floppy_interrupt(int irq, struct pt_regs *regs)
do_floppy = NULL;
if (!handler)
handler = unexpected_floppy_interrupt;
//printk("$");
handler();
}

Expand Down
6 changes: 6 additions & 0 deletions elks/fs/block_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ size_t block_read(struct inode *inode, struct file *filp, char *buf, size_t coun
if (pos <= 0)
return 0; /* EOF */

if (check_disk_change(inode->i_rdev))
return -ENXIO;

if ((loff_t)count > pos) count = (size_t)pos;

while (count > 0) {
Expand Down Expand Up @@ -70,6 +73,9 @@ size_t block_write(struct inode *inode, struct file *filp, char *buf, size_t cou
size_t chars, offset;
size_t written = 0;

if (check_disk_change(inode->i_rdev))
return -ENXIO;

if (filp->f_flags & O_APPEND) filp->f_pos = (loff_t)inode->i_size;

while (count > 0) {
Expand Down
Loading

0 comments on commit d68c81b

Please sign in to comment.