diff options
author | Jens Axboe <jens.axboe@oracle.com> | 2007-07-16 15:17:16 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2007-10-16 05:08:51 -0400 |
commit | 70eb8040dc81212c884a464b75e37dca8014f3ad (patch) | |
tree | 79d23b3ef2635032e6060dfa9b06e4f885a8cb10 /include/linux/scatterlist.h | |
parent | c6132da1704be252ee6c923f47501083d835c238 (diff) |
Add chained sg support to linux/scatterlist.h
The core of the patch - allow the last sg element in a scatterlist
table to point to the start of a new table. We overload the LSB of
the page pointer to indicate whether this is a valid sg entry, or
merely a link to the next list.
Includes a fix from Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
correcting the ifdef ARCH_HAS_SG_CHAIN guarding sg_last().
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'include/linux/scatterlist.h')
-rw-r--r-- | include/linux/scatterlist.h | 79 |
1 files changed, 77 insertions, 2 deletions
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index bed5ab4d756a..2dc7464cce52 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h | |||
@@ -20,8 +20,36 @@ static inline void sg_init_one(struct scatterlist *sg, const void *buf, | |||
20 | sg_set_buf(sg, buf, buflen); | 20 | sg_set_buf(sg, buf, buflen); |
21 | } | 21 | } |
22 | 22 | ||
23 | #define sg_next(sg) ((sg) + 1) | 23 | /* |
24 | #define sg_last(sg, nents) (&(sg[(nents) - 1])) | 24 | * We overload the LSB of the page pointer to indicate whether it's |
25 | * a valid sg entry, or whether it points to the start of a new scatterlist. | ||
26 | * Those low bits are there for everyone! (thanks mason :-) | ||
27 | */ | ||
28 | #define sg_is_chain(sg) ((unsigned long) (sg)->page & 0x01) | ||
29 | #define sg_chain_ptr(sg) \ | ||
30 | ((struct scatterlist *) ((unsigned long) (sg)->page & ~0x01)) | ||
31 | |||
32 | /** | ||
33 | * sg_next - return the next scatterlist entry in a list | ||
34 | * @sg: The current sg entry | ||
35 | * | ||
36 | * Usually the next entry will be @sg@ + 1, but if this sg element is part | ||
37 | * of a chained scatterlist, it could jump to the start of a new | ||
38 | * scatterlist array. | ||
39 | * | ||
40 | * Note that the caller must ensure that there are further entries after | ||
41 | * the current entry, this function will NOT return NULL for an end-of-list. | ||
42 | * | ||
43 | */ | ||
44 | static inline struct scatterlist *sg_next(struct scatterlist *sg) | ||
45 | { | ||
46 | sg++; | ||
47 | |||
48 | if (unlikely(sg_is_chain(sg))) | ||
49 | sg = sg_chain_ptr(sg); | ||
50 | |||
51 | return sg; | ||
52 | } | ||
25 | 53 | ||
26 | /* | 54 | /* |
27 | * Loop over each sg element, following the pointer to a new list if necessary | 55 | * Loop over each sg element, following the pointer to a new list if necessary |
@@ -29,4 +57,51 @@ static inline void sg_init_one(struct scatterlist *sg, const void *buf, | |||
29 | #define for_each_sg(sglist, sg, nr, __i) \ | 57 | #define for_each_sg(sglist, sg, nr, __i) \ |
30 | for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) | 58 | for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) |
31 | 59 | ||
60 | /** | ||
61 | * sg_last - return the last scatterlist entry in a list | ||
62 | * @sgl: First entry in the scatterlist | ||
63 | * @nents: Number of entries in the scatterlist | ||
64 | * | ||
65 | * Should only be used casually, it (currently) scan the entire list | ||
66 | * to get the last entry. | ||
67 | * | ||
68 | * Note that the @sgl@ pointer passed in need not be the first one, | ||
69 | * the important bit is that @nents@ denotes the number of entries that | ||
70 | * exist from @sgl@. | ||
71 | * | ||
72 | */ | ||
73 | static inline struct scatterlist *sg_last(struct scatterlist *sgl, | ||
74 | unsigned int nents) | ||
75 | { | ||
76 | #ifndef ARCH_HAS_SG_CHAIN | ||
77 | struct scatterlist *ret = &sgl[nents - 1]; | ||
78 | #else | ||
79 | struct scatterlist *sg, *ret = NULL; | ||
80 | int i; | ||
81 | |||
82 | for_each_sg(sgl, sg, nents, i) | ||
83 | ret = sg; | ||
84 | |||
85 | #endif | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * sg_chain - Chain two sglists together | ||
91 | * @prv: First scatterlist | ||
92 | * @prv_nents: Number of entries in prv | ||
93 | * @sgl: Second scatterlist | ||
94 | * | ||
95 | * Links @prv@ and @sgl@ together, to form a longer scatterlist. | ||
96 | * | ||
97 | */ | ||
98 | static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, | ||
99 | struct scatterlist *sgl) | ||
100 | { | ||
101 | #ifndef ARCH_HAS_SG_CHAIN | ||
102 | BUG(); | ||
103 | #endif | ||
104 | prv[prv_nents - 1].page = (struct page *) ((unsigned long) sgl | 0x01); | ||
105 | } | ||
106 | |||
32 | #endif /* _LINUX_SCATTERLIST_H */ | 107 | #endif /* _LINUX_SCATTERLIST_H */ |