Hi all, I found some races in the AIO call back path which is the root cause for a few problems in the AIO code. The race is between the aio call back path ( executed by softirqs ) and the aio_run_iocb(). (1) Suppose a retry returned -EIOCBRETRY and wait has been queued. Now, whenever the wait event has been completed, the aio_wake_function() is invoked by the softirqs. aio_wake_function deletes the wait entry (iocb->ki_wait.task_list) and invokes kick_iocb() to kick the iocb and queue it back to the run_list. Consider a situation like: SOFTIRQ: list_del_init(&wait->task_list); ## SOFTIRQ gets scheduled or PROCESS executes on another CPU ## kick_iocb(iocb); PROCESS: /* * Issue an additional retry to avoid waiting forever if * no waits were queued (e.g. in case of a short read). */ if (list_empty(&iocb->ki_wait.task_list)) kiocbSetKicked(iocb); So, PROCESS might see the deleted(empty) wait list (iocb->ki_wait.task_list) and will kick and queue it up and it will be retried again. Now if the next retry is going to happen before SOFTIRQ gets a chance to run, we are running into problems. There are two possiblities: (a) The iocb may get completed. If this happens before the SOFTIRQ enters kick_iocb(iocb), we are about to kick an iocb which is no more valid. This might cause a Kernel Oops while trying, spin_lock_irqsave(&ctx->ctx_lock, flags); since the iocb->ki_ctx may be invalid. (b) The iocb might encounter another wait condition. This case iocb would be queued for some events. This would cause the SOFTIRQ hit WARN_ON((!list_empty(&iocb->ki_wait.task_list))); in queue_kicked_iocb(). (2) Similar races occur due to Kicking the iocb. i.e, SOFTIRQ: [in kick_iocb()] if(!kiocbTryKick(iocb)) { # If we get scheduled here. And PROCESS reaches as below # queue_kicked_iocb(iocb); PROCESS: [ in aio_run_iocb() ] /* will make __queue_kicked_iocb succeed from here on */ INIT_LIST_HEAD(&iocb->ki_run_list); /* we must queue the next iteration ourselves, if it * has already been kicked */ if (kiocbIsKicked(iocb)) { <- Will be true see ( kicked by kick_iocb) __queue_kicked_iocb(iocb); } Thus iocb will get queued and may be retried. This again can cause the problems described above. Conclusion =========== From this I conclude that the following operations in call back path for async iocbs should be atomic. (*) Deletion of the wait queue entry. (*) Kicking the iocb. (*) Queue back to run_list. ( which is already atomic with ctx_lock held ). Also the check for waits being queued in aio_run_iocb() should be done with the ctx_lock held. Solution ======== I have created a patch which makes above operations to be done with the iocb->ki_ctx->ctx_lock held. Also, I have moved the check : > if (list_empty(&iocb->ki_wait.task_list)) > kiocbSetKicked(iocb); to be done under the ctx_lock held. Since we already holds a lock before calling queue_kicked_iocb(), there is no need for a queue_kicked_iocb anymore. So I have renamed the __queue_kicked_iocb to queue_kicked_iocb. Testing ======== I tested the proposed patch in 2.6.13-rc3-mm1 on a 2-way Intel Xeon(with HT) with aio-stress and fsx-linux test cases. The tests ran fine. Comments ? regards, Suzuki K P Linux Technology Centre IBM India Software Labs Bangalore. "Sorrow keeps u Human ,Failure Keeps u Humble,Success keeps u Glowing. But only God Keeps u Going..... "