函数源码

Linux Kernel

v5.5.9

Brick Technologies Co., Ltd

Source File:lib\scatterlist.c Create Date:2022-07-27 07:17:44
首页 Copyright©Brick

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/**
 * __sg_alloc_table_from_pages - Allocate and initialize an sg table from
 *                   an array of pages
 * @sgt:     The sg table header to use
 * @pages:   Pointer to an array of page pointers
 * @n_pages:     Number of pages in the pages array
 * @offset:      Offset from start of the first page to the start of a buffer
 * @size:        Number of valid bytes in the buffer (after offset)
 * @max_segment: Maximum size of a scatterlist node in bytes (page aligned)
 * @gfp_mask:    GFP allocation mask
 *
 *  Description:
 *    Allocate and initialize an sg table from a list of pages. Contiguous
 *    ranges of the pages are squashed into a single scatterlist node up to the
 *    maximum size specified in @max_segment. An user may provide an offset at a
 *    start and a size of valid data in a buffer specified by the page array.
 *    The returned sg table is released by sg_free_table.
 *
 * Returns:
 *   0 on success, negative error on failure
 */
int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
                unsigned int n_pages, unsigned int offset,
                unsigned long size, unsigned int max_segment,
                gfp_t gfp_mask)
{
    unsigned int chunks, cur_page, seg_len, i;
    int ret;
    struct scatterlist *s;
 
    if (WARN_ON(!max_segment || offset_in_page(max_segment)))
        return -EINVAL;
 
    /* compute number of contiguous chunks */
    chunks = 1;
    seg_len = 0;
    for (i = 1; i < n_pages; i++) {
        seg_len += PAGE_SIZE;
        if (seg_len >= max_segment ||
            page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) {
            chunks++;
            seg_len = 0;
        }
    }
 
    ret = sg_alloc_table(sgt, chunks, gfp_mask);
    if (unlikely(ret))
        return ret;
 
    /* merging chunks and putting them into the scatterlist */
    cur_page = 0;
    for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
        unsigned int j, chunk_size;
 
        /* look for the end of the current chunk */
        seg_len = 0;
        for (j = cur_page + 1; j < n_pages; j++) {
            seg_len += PAGE_SIZE;
            if (seg_len >= max_segment ||
                page_to_pfn(pages[j]) !=
                page_to_pfn(pages[j - 1]) + 1)
                break;
        }
 
        chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset;
        sg_set_page(s, pages[cur_page],
                min_t(unsigned long, size, chunk_size), offset);
        size -= chunk_size;
        offset = 0;
        cur_page = j;
    }
 
    return 0;
}