Copyright (c) Hyperion Entertainment and contributors.
Supporting Disk Change Events
Revision as of 16:42, 29 September 2014 by Steven Solie (talk | contribs) (Created page with "= Notes for client software, such as file systems = Device drivers either do not implement the TD_ADDCHANGEINT/TD_REMCHANGEINT commands at all, or they implement them in a ma...")
Notes for client software, such as file systems
Device drivers either do not implement the TD_ADDCHANGEINT/TD_REMCHANGEINT commands at all, or they implement them in a manner which is inconsistent with how device I/O commands are normally used. If you use these commands in your own software, then you should follow a specific protocol so as to minimize the risk of causing your software to hang or to crash the device driver.
- You must use the same 'struct IOStdReq' both for installing the change interrupt handler with TD_ADDCHANGEINT and for removing it later using TD_REMCHANGEINT. The IOStdReq->io_Data and IOStdReq->io_Length fields should not change between the use of the TD_ADDCHANGEINT/TD_REMCHANGEINT commands.
- You must not free the IOStdReq you used with the TD_ADDCHANGEINT command until after you have removed the change interrupt handler with the TD_REMCHANGEINT command.
- After you have sent the TD_ADDCHANGEINT command you must not use the IOStdReq for any other purpose except for eventually performing the TD_REMCHANGEINT command, which removes the change interrupt handler.
- Never use DoIO() with the TD_ADDCHANGEINT command. Either use SendIO(), or if you must use BeginIO() then you must make sure that the IOB_QUICK flag in the IOStdReq->io_Flags field is not set, like so:
change_request->io_Flags &= ~IOF_QUICK;
- When you remove the change interrupt handler with the TD_REMCHANGEINT command, you must make sure that the TD_ADDCHANGEINT command was successful, and if not, then you will have to remove the IOStdReq, like so:
/* Was the TD_ADDCHANGEINT command successful? */ if(IExec->CheckIO((struct IORequest *)change_request) == NULL) { change_request->io_Command = TD_REMCHANGEINT; IExec->DoIO((struct IORequest *)change_request); } /* The TD_ADDCHANGEINT command was not successful and has been rejected. */ else { IExec->WaitIO((struct IORequest *)change_request); }
- Never use SendIO() with the TD_REMCHANGEINT command. Always use DoIO(), and if you must use BeginIO() then you must make sure that the IOB_QUICK flag in the IOStdReq->io_Flags field is set, like so:
change_request->io_Flags |= IOF_QUICK;
- Never use the TD_REMCHANGEINT command unless you have used the TD_ADDCHANGEINT command first.
Notes for device driver implementors
Client software which uses the TD_ADDCHANGEINT/TD_REMCHANGEINT makes assumptions about how these commands act. The basic assumption is that none of these commands can fail, which is why your device driver should implement these commands in a specific fashion.
- The TD_ADDCHANGEINT command may be received with the IOB_QUICK flag set in the IOStdReq->io_Flags field. You must initialize the IOStdReq fields as follows and then store the IOStdReq in a list for later use:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT command are expected to be stored in a list. */ struct List * change_request_list; change_request->io_Message.mn_Node.ln_Type = NT_MESSAGE; change_request->io_Flags &= ~IOF_QUICK; change_request->io_Error = 0; /* Before you start accessing the IOStdReq list you will want to protect yourself against list modification. In this example we use Forbid() and Permit(); you might want to use a different method. Make sure to protect adding/removing IOStdReqs in the same manner. */ IExec->Forbid(); IExec->AddTail(change_request_list,&change_request->io_Message.mn_Node); IExec->Permit();
- You may want to verify that the contents of the IOStdReq->io_Data field are sound, i.e. they refer to a valid address space and the 'struct Interrupt' pointed to contains a valid Interrupt->is_Code field.
- When a medium change is detected, you should invoke each interrupt change handlers which were installed through the TD_ADDCHANGEINT command in the following fashion:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT command are expected to be stored in a list. */ struct List * change_request_list; struct IOStdReq * req; /* Before you start accessing the IOStdReq list you will want to protect yourself against list modification. In this example we use Forbid() and Permit(); you might want to use a different method. Make sure to protect adding/removing IOStdReqs in the same manner. */ IExec->Forbid(); for(req = (struct IOStdReq *)req->lh_Head ; req->io_Message.mn_Node.ln_Succ != NULL ; req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ) { IExec->Cause((struct Interrupt *)req->io_Data); } IExec->Permit();
- The TD_REMCHANGEINT command does not conform to the rules of how device commands are normally handled. The TD_REMCHANGEINT command must always be processed as if the IOB_QUICK flag were set in the IOStdReq->io_Flags field, regardless of whether this is actually the case. You must never store the IOStdReq in a list for later processing, you must always process it immediately, like so:
/* All IOStdReqs which were installed with the TD_ADDCHANGEINT command are expected to be stored in a list. */ struct List * change_request_list; struct IOStdReq * req; /* This is the IOStdReq which refers to the TD_REMCHANGEINT command which we need to perform now. */ struct IOStdReq * change_request; /* Make sure that the TD_REMCHANGEINT command refers to an IOStdReq which has been used. */ IExec->Forbid(); for(req = (struct IOStdReq *)req->lh_Head ; req->io_Message.mn_Node.ln_Succ != NULL ; req = (struct IOStdReq *)req->io_Message.mn_Node.ln_Succ) { if(req == change_request) { IExec->Remove(&change_request->io_Message.mn_Node); if((change_request->io_Flags & IOF_QUICK) == 0) IExec->ReplyMsg(&change_request->io_Message); break; } } IExec->Permit();
- It is not strictly necessary for all storage device drivers to implement the TD_ADDCHANGEINT/TD_REMCHANGEINT commands. Because existing client software assumes that TD_ADDCHANGEINT cannot fail, it may fail to clean up after a failed TD_ADDCHANGEINT command and hang/crash. For this reason you may want to consider implementing both TD_ADDCHANGEINT and TD_REMCHANGEINT commands even if your device driver does not support media change operations.