0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 #include <linux/highmem.h>
0034 #include <linux/gfp.h>
0035 #include <linux/cpu.h>
0036 #include <linux/export.h>
0037
0038 #include "rds.h"
0039
0040 struct rds_page_remainder {
0041 struct page *r_page;
0042 unsigned long r_offset;
0043 };
0044
0045 static
0046 DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_page_remainder, rds_page_remainders);
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068 int rds_page_remainder_alloc(struct scatterlist *scat, unsigned long bytes,
0069 gfp_t gfp)
0070 {
0071 struct rds_page_remainder *rem;
0072 unsigned long flags;
0073 struct page *page;
0074 int ret;
0075
0076 gfp |= __GFP_HIGHMEM;
0077
0078
0079 if (bytes >= PAGE_SIZE) {
0080 page = alloc_page(gfp);
0081 if (!page) {
0082 ret = -ENOMEM;
0083 } else {
0084 sg_set_page(scat, page, PAGE_SIZE, 0);
0085 ret = 0;
0086 }
0087 goto out;
0088 }
0089
0090 rem = &per_cpu(rds_page_remainders, get_cpu());
0091 local_irq_save(flags);
0092
0093 while (1) {
0094
0095 if (rem->r_page && bytes > (PAGE_SIZE - rem->r_offset)) {
0096 rds_stats_inc(s_page_remainder_miss);
0097 __free_page(rem->r_page);
0098 rem->r_page = NULL;
0099 }
0100
0101
0102 if (rem->r_page && bytes <= (PAGE_SIZE - rem->r_offset)) {
0103 sg_set_page(scat, rem->r_page, bytes, rem->r_offset);
0104 get_page(sg_page(scat));
0105
0106 if (rem->r_offset != 0)
0107 rds_stats_inc(s_page_remainder_hit);
0108
0109 rem->r_offset += ALIGN(bytes, 8);
0110 if (rem->r_offset >= PAGE_SIZE) {
0111 __free_page(rem->r_page);
0112 rem->r_page = NULL;
0113 }
0114 ret = 0;
0115 break;
0116 }
0117
0118
0119 local_irq_restore(flags);
0120 put_cpu();
0121
0122 page = alloc_page(gfp);
0123
0124 rem = &per_cpu(rds_page_remainders, get_cpu());
0125 local_irq_save(flags);
0126
0127 if (!page) {
0128 ret = -ENOMEM;
0129 break;
0130 }
0131
0132
0133 if (rem->r_page) {
0134 __free_page(page);
0135 continue;
0136 }
0137
0138
0139 rem->r_page = page;
0140 rem->r_offset = 0;
0141 }
0142
0143 local_irq_restore(flags);
0144 put_cpu();
0145 out:
0146 rdsdebug("bytes %lu ret %d %p %u %u\n", bytes, ret,
0147 ret ? NULL : sg_page(scat), ret ? 0 : scat->offset,
0148 ret ? 0 : scat->length);
0149 return ret;
0150 }
0151 EXPORT_SYMBOL_GPL(rds_page_remainder_alloc);
0152
0153 void rds_page_exit(void)
0154 {
0155 unsigned int cpu;
0156
0157 for_each_possible_cpu(cpu) {
0158 struct rds_page_remainder *rem;
0159
0160 rem = &per_cpu(rds_page_remainders, cpu);
0161 rdsdebug("cpu %u\n", cpu);
0162
0163 if (rem->r_page)
0164 __free_page(rem->r_page);
0165 rem->r_page = NULL;
0166 }
0167 }