The chained scatterlist API

Personal Computer/Linux 2018. 3. 19. 21:13 posted by tolkien

https://lwn.net/Articles/256368/


When asked which of the changes in 2.6.24 was most likely to create problems, an informed observer might well point at the i386/x86_64 merger. As it happens, that large patch set has gone in with relatively few hitches, but a rather smaller change has created quite a bit of fallout. The change in question is the updated API for the management of scatterlists, which are used in scatter/gather I/O. This work broke a number of in-tree drivers, so it seems likely to affect a lot of out-of-tree code as well.



Scatter / gather I/O는 시스템이 실제 메모리에 분산되어있는 버퍼에 대해 DMA I / O 작업을 수행 할 수있게합니다. 예를 들어, 사용자 공간에서 생성 된 대형 (다중 페이지) 버퍼의 경우를 생각해보십시오. 응용 프로그램은 연속적인 범위의 가상 주소를 보지만 그 주소 뒤에 있는 물리적 페이지는 거의 서로 인접하지 않습니다. 단일 I / O 작업에서 해당 버퍼를 장치에 기록하려면 다음 두 가지 중 하나를 수행해야합니다. (1) 데이터를 물리적으로 인접한 버퍼에 복사해야하거나 (2) 장치가 물리적 주소 및 길이 목록을 사용하여 각 세그먼트에서 올바른 양의 데이터를 가져옵니다. Scatter / gatter I/O는, 연속적인 버퍼로 데이터를 복사 할 필요를 제거함으로써,  I/O 작업의 효율성을 크게 높일 수있을뿐만 아니라 물리적으로 연속적인 대형 버퍼의 생성이 처음에는 문제가 될 수 있다는 문제를 해결할 수 있습니다.


커널 내에서, scatter / gather DMA 연산에 사용될 버퍼는 <linux / scatterlist.h>에 정의 된 하나 이상의 scatterlist 구조의 배열로 표현됩니다. 이 배열은 전통적으로 단일 페이지 내에 들어가도록 제한되어있어 분산 / 수집 작업에 최대 길이를 부여합니다. 이 한계는 하이 엔드 시스템에서 병목 현상이되는 것으로 나타났습니다. 그렇지 않으면 대용량 버퍼 (일반적으로 디스크 장치간에)를 전송할 때 이점이 있습니다. 결과적으로 그 한계를 극복 할 수있는 방안이 모색되고 있습니다. 간혹 mailing list에 나타나는 대용량 블록 크기 패치가 한 가지 방법입니다. 그러나 커널을 2.6.24 커널로 만들었던 해법은 Scatter / gatter list의 길이를 묶어서(chained) 제거하는 것입니다.


Chained Scatter / gatter list는 둘 이상의 페이지로 구성 될 수 있으며 해당 페이지도 실제 메모리에 흩어질 수 있습니다. 이 체인(chaining)이 완료되면 버퍼 포인터의 하위 비트 두 개를 사용하여 체인 항목과 목록의 끝을 표시합니다. 이 사용법은 드라이버 코드에서 걱정할 필요가 있는 것이 아니지만 특수 bit와 체인 pointer가 있으면 드라이버가 scatter list에서 작동하는 방식을 일부 변경해야합니다.



연결(chaining)을 수행하지 않는 드라이버는 일반적으로 kcalloc () 또는 일부 호출을 통해 scatterlist array를 일반적인 방식으로 할당합니다. 2.6.23 이전에는 초기화 단계가 필요하지 않았으며, 아마도 전체 배열을 초기화하지 않았습니다. 그러나 그것은 바뀌었습니다. 드라이버는 다음과 같이 scatterlist array를 초기화해야합니다:


   void sg_init_table(struct scatterlist *sg, unsigned int nents);

여기서 sg 는 할당 된 배열을 가리키고, nents 는 할당 된 scatter/gather entry들의 갯수입니다.


이전과 마찬가지로, 드라이버는 버퍼의 세그먼트를 반복하여 각각에 대해 하나의 scatterlist 항목을 설정해야합니다. 그러나 더 이상 페이지 포인터를 직접 설정할 수는 없습니다: 그 포인터는 2.6.24에 존재하지 않습니다. 대신, scatterlist 항목을 설정하는 일반적인 방법은 다음 중 하나입니다:



  void sg_set_page(struct scatterlist *sg, struct page *page,
            unsigned int len, unsigned int offset);

   void sg_set_buf(struct scatterlist *sg, const void *buf,
                unsigned int buflen);

2.6.24 scatterlist는 또한 목록의 끝 부분에 명시적으로 표시해야합니다. 이 표시는 sg_init_table () 이 호출 될 때 수행되므로, 드라이버는 일반적으로 명시적으로 끝을 표시하지 않아도됩니다. I/O 작업이 목록에 할당된 모든 항목을 사용하지 않으면, 드라이버는 최종 세그먼트를 다음처럼 표시해야합니다:


   void sg_mark_end(struct scatterlist *sg, unsigned int nents);

여기서 nents 는 scatterlist의 유효한 항목 수입니다.


scatterlist가 매핑 된 후 ( dma_map_sg () 와 같은 함수를 사용하여), 드라이버는 결과 DMA 주소를 하드웨어에 프로그래밍해야합니다. 배열을 단계적으로 실행하는 이전 접근 방식은 더 이상 작동하지 않습니다. 대신 드라이버는 다음을 사용하여 scatterlist의 다음 항목으로 이동해야합니다:


   struct scatterlist *sg_next(struct scatterlist *sg);

반환 값은 처리 할 다음 항목이되거나 목록의 끝에 도달하면 NULL이 됩니다. 또한 전체 scatterlist를 반복하는 데 사용할 수있는 for_each_sg () 매크로가 있습니다. 일반적으로 다음과 같은 코드에서 사용됩니다:



    int i;
   struct scatterlist *list, *sgentry;

   /* Fill in list and pass it to dma_map_sg().  Then... */
   for_each_sg(i, list, sgentry, nentries) {
    program_hw(device, sg_dma_address(sgentry), sg_dma_len(sgentry));
   }


chaining feature를 이용하려는 드라이버는 조금만 더 작업해야합니다. scatterlist의 각 조각은 독립적으로 할당되어야하며 그 조각은 다음과 함께 체인(chained)되어야합니다.


   void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
         struct scatterlist *next);

이 호출은 scatterlist 엔트리 prv [nents] 를 next 에 대한 체인 링크로 변환합니다. 목록이 채워지는 동안 연결이 완료되면, prv 에는 prv_nents-1 세그먼트가 저장되어 있어야합니다. 또는 드라이버는 미리 목록의 조각을 함께 묶을 수 있습니다 (각 체인 링크마다 하나의 항목을 할당하는 것을 기억하십시오). 그런 다음 sg_next () 를 사용하여 체인 링크가 어디에 있는지 걱정할 필요없이 목록을 채웁니다.


이 글을 쓰는 시점에서,이 API는 in-tree 드라이버와 관련된 문제에 대응하여 계속 진화하고 있습니다. 2.6.24 릴리즈 이전에는 더 이상의 실질적인 변경이 이루어질 것 같지 않지만, 변경이 될 수도 있습니다.

O_NOMTIME

Personal Computer/Linux 2015. 5. 26. 23:31 posted by tolkien

http://lwn.net/Articles/643892/


mtime은 file 내용이 수정된 마지막 시간을 가리킵니다. 최근에 mtime을 자동으로 update하지 않도록 하는 patch가 올라왔습니다.
이는 Ceph filesystem(http://lwn.net/Articles/258516/)과 같이 mtime을 사용하지 않는 경우, 성능을 향상시키는 효과가 있습니다. 하지만, NFS나 system backup과 같이 mtime을 이용하는 경우, 이 option은 매우 위험한 선택입니다. 그리고, system suspend와 같은 상황에서는 더욱 복잡해집니다.

어쨌든 O_NOMTIME option은 손쉽게 system 성능을 개선할 수 있습니다만,
application 개발자가 sync하는 것을 잊는 경우, file system에 손상을 줄 수 있습니다.

Persistent memory and page structures

Personal Computer/Linux 2015. 5. 26. 23:15 posted by tolkien

http://lwn.net/Articles/644079/


persistent memory(or non-volatile memory), 쉽게 NOR flash를 생각하면될 것같습니다. SSD에 대해서 direct-access를 하는 경우도 있을지도.

어쨌든 이 유형의 h/w는 cpu에서 직접 접근가능하다.라는 이점이 있어서 이것을 이용할 수 있는 방법에 대한 논의를 간단히 다룬 내용입니다.

단순하게 ROM처럼 page로 분할해서 mapping해버리면 1TB가 넘는 경우, 관리하는 데만많은 memory를 사용하므로 좋지 않습니다. 이에...
1. persistent memory에 대해서 Page Frame Number(PFN)을 사용.
 - http://lwn.net/Articles/643437/ - by Dan Williams
 - page structure를 손대서 persistent memory인 경우, page가 아닌 PFN을 사용
2. Directly mapped persistent memory page cache
 - http://lwn.net/Articles/644114/ - by Ingo Molnar
 - page structure를 일반 memory가 아닌 persistent memory에 저장

아직 어떤 방법도 code로 다 구현되어 있지 않고, 논의중입니다.