/*
* Oplock translator:
*
* Serialize rmw requests submission with a quarantee
* of progress.
* Maintain per-inode queues of submission requests;
*
*
* Glossary:
*
* Read-modify-write request (rmw-request)
*
* is a write request, which contains extra-data
* to write. Such requests are specific to atomic
* cipher modes.
* Submitting an rmw request can make other pending
* rmw-requests out-of-date, and it will lead to loss
* of operations. In order to prevent this we serialize
* rmw-requests submission and make sure that every
* request which is going to be submitted contains
* uptodate information.
*
* Request for submission (sub-request)
*
* An element in inode's rmw_queue. A sub-request is
* issued for every new rmw-request in the case when
* the file is "busy" (i.e. the rmw_queue is not empty).
* In this case a sub-request with a unique serial number
* is issued and put to the rmw-queue. With this number
* the process will uptodate and try to re-submit the
* rmw request later.
*
*
*
* struct _inode {
* ...
*
* gf_lock_t rmwq_lock; /* protects the rmw-queue */
* rmw_queue_head_t head; /* queue of submission requests */
* uint64_t generation; /* last allocated serial number */
* }
*
* lock_queue(inode) is LOCK(&inode->rmwq_lock)
* unlock_queue(inode) is UNLOCK(&inode->rmwq_lock)
*/
/*
* oplock_witev():
*
* Pre-condition: a read-modify-write request rmw_req
* has been received.
*
* Check if we can proceed with this.
*
* If we can not, then allocate a serial number and issue
* a sub-request if the rmw-req is new (i.e. it doesn't
* have a serial number and we didn't try to submit this
* request before). Than return error to re-submit this
* later.
*/
oplock_witev()
{
lock_queue(inode);
if (the inode's rmw_queue is empty) {
empty:
/*
* submit this rmw_req, but first
* we need to "occupy the queue"
*/
allocate a serial number;
create a sub-request and insert it to the queue";
goto submit;
}
else {
if (is_new_request(rmw_req)) {
/*
* queue is not empty, so we are not
* allowed to proceed (there are
* requests with higher priorities.
*/
allocate a serial number,
create a sub-request and insert it to the queue;
/*
* refuse to proceed, will be
* restarted with this serial
* number later
*/
goto refuse;
}
/* queue is not empty and rmw_req is
* not new (has serial number)
*/
next:
find the next sub_req in the queue;
if (sub_req is too old, timeout is over) {
/* the owner has died */
remove sub_req;
if (rmw_queue is empty)
goto empty;
else
goto next;
}
if (serial_number_of(rmw_req) != serial_number_of(next_sub_req){
/*
* this is not our turn to submit
*/
refuse:
unlock_queue(inode);
/*
* refuse to proceed,
* will be re-submitted
*/
op_errno = EBUSY;
STACK_UNWIND();
return;
}
submit:
/*
* our turn to submit,
* allow to proceed
*/
unlock_queue(inode);
STACK_WIND();
return;
}
}
/*
* this is called only if we
* were allowed to submit.
*/
oplock_writew_cbk()
{
if (op_ret > = 0) {
/*
* it was our turn to submit, and our
* rmw-request has been successfully
* submitted
*/
lock_queue(inode);
remove the sub-request from the queue;
unlock_queue(inode);
}
else {
/*
* it was our turn to submit, but error
* happened in other layers, so don't remove
* sub-request, will retry.
*/
STACK_UNWIND();
}
}
Show replies by date