更新版本的内核在这一部分的改动似乎不大)
bool __blk_end_request(struct request *rq, int error, unsigned int nr_bytes) { if (blk_update_request(rq, error, nr_bytes)) return true; blk_finish_request(rq, error); return false; }
static void blk_finish_request(struct request *req, int error) { ...... ...... // 如果设置了 req 的 end_io 成员函数,则调用定制的 end_io 函数来结束请求; // 否则,使用默认的__blk_put_request 函数释放该 request 结构。 if (req->end_io) req->end_io(req, error); else { ...... __blk_put_request(req->q, req); } }
bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) { int total_bytes, bio_nbytes, next_idx = 0; struct bio *bio; ...... ...... total_bytes = bio_nbytes = 0; // 调用此函数时,说明已经读写完了 req 中的 nr_bytes 个字节,所以要从 req 中 // 移除这些已经被读写的字节。下面的每次循环中,都会移除一部分字节,并将 nr_bytes // 减掉相应的长度。( nr_bytes始终等于剩余的待被移除的字节数 ) // 下面的循环中,会首先尝试从 req 中依次移除一个 bio ; // 如果检测到某个 bio 的 size 大于 nr_bytes 时,说明当前的 bio 还没有被完全读写完毕, // 这时会再尝试移除该bio中的每个 bio_vec , 当检测到某个bio_vec的size大于nr_bytes // 时,会通过调整该 bio_vec 数据区域的长度和起始地址来移除已被读写的字节。 // 即: 在未遇到不完整的 bio 时,每次循环移除一个 bio ; // 遇到不完正的 bio 后,未遇到不完整的 bio_vec 前,每次移除一个 bio_vec ; // 遇到不完整的 bio_vec 时,退出循环; while ((bio = req->bio) != NULL) { int nbytes; // nbytes是本次循环中将被移除的数据长度,total_bytes是req中已被移除的字节数 // 当遇到不完整的bio后, next_idx表示本次循环所处理的bio_vec在该bio中的索引偏移(相对于bio->bi_idx), // bio_nbytes是该bio中已被移除的字节数 if (nr_bytes >= bio->bi_size) { // 即将被结束的字节数量大于当前bio的字节数,则直接结束当前的bio,并设置next_idx和bio_nbytes为0 req->bio = bio->bi_next; nbytes = bio->bi_size; req_bio_endio(req, bio, nbytes, error); // 结束当前 bio next_idx = 0; bio_nbytes = 0; } else { // nr_bytes < bio->bi_size , 说明遇到了未被完整读写的 bio int idx = bio->bi_idx + next_idx; nbytes = bio_iovec_idx(bio, idx)->bv_len; // 获取当期 bio_vec 的数据长度nbytes if (unlikely(nbytes > nr_bytes)) { // nbytes > nr_bytes 说明当前的这一个bio_vec未被完整的读写 /* * not a complete bvec done */ bio_nbytes += nr_bytes; total_bytes += nr_bytes; break; //这里直接退出大循环 } else { // 如果当前的这一个bio_vec被完整的读写了,则next_idx指向下一个bio_vec,同时将bio_nbytes加上nbytes /* * advance to the next vector */ next_idx++; bio_nbytes += nbytes; } } total_bytes += nbytes; nr_bytes -= nbytes; // 已移除 nbytes 个字节, 所以减去 nbytes bio = req->bio; if (bio) { /* * end more in this run, or just return 'not-done' */ if (unlikely(nr_bytes <= 0)) break; // 这里说明 nr_bytes 个字节已经全部被移除了,跳出大循环 } } // 大循环结束。(nr_bytes个字节移除完毕) /* * completely done */ if (!req->bio) { // 没有剩余的bio,说明一个请求被完整地完成了 /* * Reset counters so that the request stacking driver * can find how many bytes remain in the request * later. */ req->__data_len = 0; return false; // return false 表示整个 request 已经处理完毕 } /* * if the request wasn't completed, update state */ // request没有完全完成,这时更新该request的信息 if (bio_nbytes) { req_bio_endio(req, bio, bio_nbytes, error); // 将当前bio结束 bio_nbytes 个字节 bio->bi_idx += next_idx; // 更新当前 bio 的 bio_vec 指针。(指向下一个将要被读写的bio_vec) bio_iovec(bio)->bv_offset += nr_bytes; // 更新当前 bio_vec 的数据指针和数据长度 bio_iovec(bio)->bv_len -= nr_bytes; } req->__data_len -= total_bytes; // 设置 request 的当前段数据长度 __data_len 和 当前段数据指针 buffer . req->buffer = bio_data(req->bio); /* update sector only for requests with clear definition of sector */ if (req->cmd_type == REQ_TYPE_FS || (req->cmd_flags & REQ_DISCARD)) req->__sector += total_bytes >> 9; // 设置request的当前扇区号。 从这一句可以看出一个请求中的所有 bio 的介质地址都是连续的 return true; // return true 表示 request 没有完全处理完。 } // 上面的函数中多次出现了 req_bio_endio 来结束(或部分结束)一个 bio ,下面看下这个函数都干了什么 static void req_bio_endio(struct request *rq, struct bio *bio, unsigned int nbytes, int error) { ...... ...... bio->bi_size -= nbytes; // 更新 bio 的数据长度和起始扇区号 bio->bi_sector += (nbytes >> 9); if (bio_integrity(bio)) bio_integrity_advance(bio, nbytes); // 与 integrity data 相关的函数和结构体我还没有研究过, // 不过bio_integrity_advance这个函数只对bio->bi_integrity // 进行操作,不会改变 bio 的其他重要成员 /* don't actually finish bio if it's part of flush sequence */ if (bio->bi_size == 0 && !(rq->cmd_flags & REQ_FLUSH_SEQ)) bio_endio(bio, error); // 如果bio被完成了(size=0),调用bio_endio } // bio_endio 函数中主要是对 bio->bi_end_io 的调用。疑点: 一个 bio 处理完后在哪里释放其所占用的内存? void bio_endio(struct bio *bio, int error) { ...... ...... if (bio->bi_end_io) bio->bi_end_io(bio, error); }
bool __blk_end_request(struct request *rq, int error, unsigned int nr_bytes) { return __blk_end_bidi_request(rq, error, nr_bytes, 0); } static bool __blk_end_bidi_request(struct request *rq, int error, unsigned int nr_bytes, unsigned int bidi_bytes) { if (blk_update_bidi_request(rq, error, nr_bytes, bidi_bytes)) return true; blk_finish_request(rq, error); return false; } static bool blk_update_bidi_request(struct request *rq, int error, unsigned int nr_bytes, unsigned int bidi_bytes) { if (blk_update_request(rq, error, nr_bytes)) return true; /* Bidi request must be completed as a whole */ if (unlikely(blk_bidi_rq(rq)) && blk_update_request(rq->next_rq, error, bidi_bytes)) return true; if (blk_queue_add_random(rq->q)) add_disk_randomness(rq->rq_disk); return false; }