Tuesday, 27 January 2015

Read system call trace (contd.)

As discussed in my previous post I would like to continue the Read system call  flow from submit_bio in this post.

The submit_bio() function calls the generic_make_request() function to submit the bio to the block device layer for I/O

generic_make_request(): This transfers the bio to the corresponding device driver. The function receives a pointer to the bio structure which is passed to the submit_bio function from the filesystem layer. This function is used to make the IO request for the block device. Its return type is void because the result of successful completion of IO is returned through the bi_end_io function pointer whose address is stored in the bio structure.

generic_make_request() does the following :

  1. Perform some checks on this bio
  2. Add the bio to the list of bios maintained by current if the queue is not empty(this is a bit confusing..why will there be a queue of bios?? shouldnt the the bio be submitted to the scsi layer before current selects the next bio??)
  3. If this is the first bio for the current process then create a list of bios.
  4. Do the following for all the bios in the list:
    a) Get the request queue of the block device.
    b) Call the make_request_fn for the request queue for this bio. the make_request_fn is a function pointer which in turn calls calls the function blk_queue_bio(). The value to the request_queue structure is initialized in the function blk_init_allocated_queue(). Need to look in the initialization step for this function
    c) Remove that request from the queue.


blk_queue_bio(request_queue, bio) -- The make request function

  1. Calls the blk_queue_bounce(). need to investigate what this does.
  2. Check for bio_integrity. If it is enabled, call the bio_integrity_prep() function. need to investigate this further.
  3. In case this is a flush request(REQ_FLUSH flag is set) or REQ_FUA flag is set, the insertion selection queue is ELEVATOR_INSERT_FLUSH.
  4. If the disable_merge flag is set on this bio, call the blk_attempt_plug_merge() function. the blk_attempt_plug_merge function will needed stuffs for block device plugging. Read the doc (http://lwn.net/Articles/438256/) for further information.
  5. Call the elv_merge() function. This function will decide where to merge this bio with the request list maintained to be dispatched.
    a) first check if the merging is needed or not. In case its not needed (QUEUE_FLAG_NOMERGES flag is set on the request_queue) return.
    b) try the next level of merging. perform some checks (performed in the function elv_rq_merge_ok()) to verify if the bio can be merged with the request. in case the tests pass, call the function blk_try_merge(). This will decide where to merge this bio - in the front, back or cannot be merged at all. The decision is taken on the current entries in the request and the current bio's sector.
    c) if the bio cannot be merged with the request, the return with the address of the request structure object pointer assigned to request_queue (i guess this will be the case for the first bio)
    d) if the bio can be merged, call the elevator_merge_fn. The function can be cfq_merge() or deadline_merge() based on the io scheduler used. The function will return whether to merge the bio at the end or at the front of the request.
  6. Based on the value returned from the elv_merge function, decide where to merge the bio.
    a) in case the bio needs to be merged at the back, call the bio_attempt_back_merge() function. this first calls the ll_back_merge_fn() which first checks if the request goes out of bound or not. (need to check more on this function). After this, add the bio to the tail of request list of bios and update the data_len and priority of the request. on successful merge of the bio to the request, call the elv_bio_merged function which will in turn call the io scheduler specific elevator_bio_merged_fn function. this is only defined for cfq and the function does the statistics updation.
    b)When the bio merge operation has been performed, call the attempt_back_merge() function. This function will check if a request is present in the rb tree (with the help of the elv_latter_request() function). In case a request exists, call the attemp_merge function. This will try to merge the request already existing with the request being worked on. To do this, some checks are performed and the bios present in requestobtained from the tree is merged with this request.
    After all this, the job is finished. release the lock and leave the blk_queue_bio function.
  7. If the request could not be merged, get a free request from the queue for the bio using the get_request function.
    a) The function first gets the request list for the queue. In case the list is not there in the request_queue, create one using the function blkg_lookup_create() (the queue is based on block cgroups if that is enabled.).
  8. Call the init_request_from_bio. This function adds the bio to the request after the request was tagged as non merge-able in the above steps. This includes setting the various fields of request structure and set the bio and biotail fields of request to the current bio.
  9. Now the request is added to the request_queue using add_acct_request() function. The code like list_add(&rq->queuelist, &q->queue_head); adds the request (rq) to the request_queue(q)
  10. if the plug list is maintained for the current task, take the queue lock, and call the __blk_run_queue() function. This function calls the request_fn pointer. For scsi, its the scsi_request_fn.
Will discuss the scsi_request_fn and the HBA interactions in the next blog. Stay tuned !!!

No comments:

Post a Comment