]> git.proxmox.com Git - mirror_spl.git/blame - module/spl/spl-generic.c
Implementation of the TQ_FRONT flag.
[mirror_spl.git] / module / spl / spl-generic.c
CommitLineData
716154c5
BB
1/*****************************************************************************\
2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 * Copyright (C) 2007 The Regents of the University of California.
4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
715f6251 6 * UCRL-CODE-235197
7 *
716154c5
BB
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
716154c5
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting Layer (SPL) Generic Implementation.
25\*****************************************************************************/
715f6251 26
14c5326c 27#include <sys/sysmacros.h>
99639e4a 28#include <sys/systeminfo.h>
af828292 29#include <sys/vmsystm.h>
30#include <sys/vnode.h>
c19c06f3 31#include <sys/kmem.h>
9ab1ac14 32#include <sys/mutex.h>
d28db80f 33#include <sys/rwlock.h>
e9cb2b4f 34#include <sys/taskq.h>
8d0f1ee9 35#include <sys/debug.h>
57d1b188 36#include <sys/proc.h>
04a479f7 37#include <sys/kstat.h>
691d2bd7 38#include <sys/utsname.h>
d3126abe 39#include <sys/file.h>
f23e92fa 40#include <linux/kmod.h>
ae4c36ad 41#include <linux/proc_compat.h>
f1b59d26 42
57d1b188 43#ifdef DEBUG_SUBSYSTEM
44#undef DEBUG_SUBSYSTEM
45#endif
8d0f1ee9 46
57d1b188 47#define DEBUG_SUBSYSTEM S_GENERIC
f23e92fa 48
0cbaeb11 49char spl_version[16] = "SPL v" SPL_META_VERSION;
3561541c 50
937879f1 51long spl_hostid = 0;
f23e92fa 52EXPORT_SYMBOL(spl_hostid);
8d0f1ee9 53
99639e4a 54char hw_serial[HW_HOSTID_LEN] = "<none>";
937879f1 55EXPORT_SYMBOL(hw_serial);
f1b59d26 56
ae4c36ad 57proc_t p0 = { 0 };
f1b59d26 58EXPORT_SYMBOL(p0);
70eadc19 59
d1ff2312 60#ifndef HAVE_KALLSYMS_LOOKUP_NAME
96dded38 61kallsyms_lookup_name_t spl_kallsyms_lookup_name_fn = SYMBOL_POISON;
d1ff2312
BB
62#endif
63
77b1fe8f 64int
65highbit(unsigned long i)
66{
67 register int h = 1;
3d061e9d 68 ENTRY;
77b1fe8f 69
70 if (i == 0)
57d1b188 71 RETURN(0);
77b1fe8f 72#if BITS_PER_LONG == 64
73 if (i & 0xffffffff00000000ul) {
74 h += 32; i >>= 32;
75 }
76#endif
77 if (i & 0xffff0000) {
78 h += 16; i >>= 16;
79 }
80 if (i & 0xff00) {
81 h += 8; i >>= 8;
82 }
83 if (i & 0xf0) {
84 h += 4; i >>= 4;
85 }
86 if (i & 0xc) {
87 h += 2; i >>= 2;
88 }
89 if (i & 0x2) {
90 h += 1;
91 }
57d1b188 92 RETURN(h);
77b1fe8f 93}
94EXPORT_SYMBOL(highbit);
95
b61a6e8b 96/*
550f1705 97 * Implementation of 64 bit division for 32-bit machines.
b61a6e8b 98 */
550f1705 99#if BITS_PER_LONG == 32
1b4ad25e
AZ
100uint64_t
101__udivdi3(uint64_t dividend, uint64_t divisor)
b61a6e8b 102{
96dded38 103#if defined(HAVE_DIV64_64) /* 2.6.22 - 2.6.25 API */
550f1705 104 return div64_64(dividend, divisor);
96dded38
BB
105#elif defined(HAVE_DIV64_U64) /* 2.6.26 - 2.6.x API */
106 return div64_u64(dividend, divisor);
550f1705 107#else
96dded38 108 /* Implementation from 2.6.30 kernel */
b61a6e8b 109 uint32_t high, d;
110
111 high = divisor >> 32;
112 if (high) {
113 unsigned int shift = fls(high);
114
115 d = divisor >> shift;
116 dividend >>= shift;
117 } else
118 d = divisor;
119
db1aa222
BB
120 do_div(dividend, d);
121
122 return dividend;
96dded38 123#endif /* HAVE_DIV64_64, HAVE_DIV64_U64 */
550f1705 124}
125EXPORT_SYMBOL(__udivdi3);
126
127/*
128 * Implementation of 64 bit modulo for 32-bit machines.
129 */
1b4ad25e
AZ
130uint64_t
131__umoddi3(uint64_t dividend, uint64_t divisor)
550f1705 132{
1b4ad25e 133 return (dividend - (divisor * __udivdi3(dividend, divisor)));
b61a6e8b 134}
550f1705 135EXPORT_SYMBOL(__umoddi3);
96dded38 136#endif /* BITS_PER_LONG */
b61a6e8b 137
b871b8cd
BB
138/* NOTE: The strtoxx behavior is solely based on my reading of the Solaris
139 * ddi_strtol(9F) man page. I have not verified the behavior of these
140 * functions against their Solaris counterparts. It is possible that I
96dded38 141 * may have misinterpreted the man page or the man page is incorrect.
b871b8cd 142 */
2ee63a54
BB
143int ddi_strtoul(const char *, char **, int, unsigned long *);
144int ddi_strtol(const char *, char **, int, long *);
145int ddi_strtoull(const char *, char **, int, unsigned long long *);
146int ddi_strtoll(const char *, char **, int, long long *);
147
148#define define_ddi_strtoux(type, valtype) \
149int ddi_strtou##type(const char *str, char **endptr, \
b871b8cd 150 int base, valtype *result) \
2ee63a54 151{ \
b871b8cd
BB
152 valtype last_value, value = 0; \
153 char *ptr = (char *)str; \
154 int flag = 1, digit; \
155 \
156 if (strlen(ptr) == 0) \
157 return EINVAL; \
158 \
159 /* Auto-detect base based on prefix */ \
160 if (!base) { \
161 if (str[0] == '0') { \
162 if (tolower(str[1])=='x' && isxdigit(str[2])) { \
163 base = 16; /* hex */ \
164 ptr += 2; \
165 } else if (str[1] >= '0' && str[1] < 8) { \
166 base = 8; /* octal */ \
167 ptr += 1; \
168 } else { \
169 return EINVAL; \
170 } \
171 } else { \
172 base = 10; /* decimal */ \
173 } \
174 } \
175 \
176 while (1) { \
177 if (isdigit(*ptr)) \
178 digit = *ptr - '0'; \
179 else if (isalpha(*ptr)) \
180 digit = tolower(*ptr) - 'a' + 10; \
181 else \
182 break; \
183 \
184 if (digit >= base) \
185 break; \
2ee63a54 186 \
b871b8cd
BB
187 last_value = value; \
188 value = value * base + digit; \
189 if (last_value > value) /* Overflow */ \
190 return ERANGE; \
2ee63a54 191 \
b871b8cd
BB
192 flag = 1; \
193 ptr++; \
2ee63a54
BB
194 } \
195 \
b871b8cd
BB
196 if (flag) \
197 *result = value; \
198 \
199 if (endptr) \
200 *endptr = (char *)(flag ? ptr : str); \
201 \
202 return 0; \
2ee63a54
BB
203} \
204
205#define define_ddi_strtox(type, valtype) \
206int ddi_strto##type(const char *str, char **endptr, \
207 int base, valtype *result) \
b871b8cd
BB
208{ \
209 int rc; \
2ee63a54
BB
210 \
211 if (*str == '-') { \
b871b8cd
BB
212 rc = ddi_strtou##type(str + 1, endptr, base, result); \
213 if (!rc) { \
214 if (*endptr == str + 1) \
215 *endptr = (char *)str; \
216 else \
217 *result = -*result; \
218 } \
2ee63a54 219 } else { \
b871b8cd 220 rc = ddi_strtou##type(str, endptr, base, result); \
2ee63a54
BB
221 } \
222 \
b871b8cd
BB
223 return rc; \
224}
2ee63a54
BB
225
226define_ddi_strtoux(l, unsigned long)
227define_ddi_strtox(l, long)
228define_ddi_strtoux(ll, unsigned long long)
229define_ddi_strtox(ll, long long)
230
2f5d55aa 231EXPORT_SYMBOL(ddi_strtoul);
2ee63a54
BB
232EXPORT_SYMBOL(ddi_strtol);
233EXPORT_SYMBOL(ddi_strtoll);
234EXPORT_SYMBOL(ddi_strtoull);
2f5d55aa 235
d3126abe
BB
236int
237ddi_copyin(const void *from, void *to, size_t len, int flags)
238{
239 /* Fake ioctl() issued by kernel, 'from' is a kernel address */
240 if (flags & FKIOCTL) {
241 memcpy(to, from, len);
242 return 0;
243 }
244
245 return copyin(from, to, len);
246}
247EXPORT_SYMBOL(ddi_copyin);
248
249int
250ddi_copyout(const void *from, void *to, size_t len, int flags)
251{
252 /* Fake ioctl() issued by kernel, 'from' is a kernel address */
253 if (flags & FKIOCTL) {
254 memcpy(to, from, len);
255 return 0;
256 }
257
258 return copyout(from, to, len);
259}
260EXPORT_SYMBOL(ddi_copyout);
261
e811949a
BB
262#ifndef HAVE_PUT_TASK_STRUCT
263/*
264 * This is only a stub function which should never be used. The SPL should
265 * never be putting away the last reference on a task structure so this will
266 * not be called. However, we still need to define it so the module does not
267 * have undefined symbol at load time. That all said if this impossible
268 * thing does somehow happen SBUG() immediately so we know about it.
269 */
270void
271__put_task_struct(struct task_struct *t)
272{
273 SBUG();
274}
275EXPORT_SYMBOL(__put_task_struct);
276#endif /* HAVE_PUT_TASK_STRUCT */
277
691d2bd7 278struct new_utsname *__utsname(void)
279{
3d061e9d 280#ifdef HAVE_INIT_UTSNAME
691d2bd7 281 return init_utsname();
3d061e9d 282#else
283 return &system_utsname;
284#endif
691d2bd7 285}
286EXPORT_SYMBOL(__utsname);
287
8d0f1ee9 288static int
57d1b188 289set_hostid(void)
8d0f1ee9 290{
f23e92fa 291 char sh_path[] = "/bin/sh";
292 char *argv[] = { sh_path,
293 "-c",
57d86234 294 "/usr/bin/hostid >/proc/sys/kernel/spl/hostid",
f23e92fa 295 NULL };
296 char *envp[] = { "HOME=/",
297 "TERM=linux",
298 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
299 NULL };
96dded38 300 int rc;
8d0f1ee9 301
57d1b188 302 /* Doing address resolution in the kernel is tricky and just
937879f1 303 * not a good idea in general. So to set the proper 'hw_serial'
57d1b188 304 * use the usermodehelper support to ask '/bin/sh' to run
305 * '/usr/bin/hostid' and redirect the result to /proc/sys/spl/hostid
96dded38 306 * for us to use. It's a horrific solution but it will do for now.
57d1b188 307 */
96dded38
BB
308 rc = call_usermodehelper(sh_path, argv, envp, 1);
309 if (rc)
310 printk("SPL: Failed user helper '%s %s %s', rc = %d\n",
311 argv[0], argv[1], argv[2], rc);
312
313 return rc;
57d1b188 314}
8d0f1ee9 315
99639e4a
BB
316uint32_t
317zone_get_hostid(void *zone)
318{
319 unsigned long hostid;
320
321 /* Only the global zone is supported */
322 ASSERT(zone == NULL);
323
324 if (ddi_strtoul(hw_serial, NULL, HW_HOSTID_LEN-1, &hostid) != 0)
325 return HW_INVALID_HOSTID;
326
327 return (uint32_t)hostid;
328}
329EXPORT_SYMBOL(zone_get_hostid);
330
96dded38 331#ifndef HAVE_KALLSYMS_LOOKUP_NAME
d1ff2312
BB
332/*
333 * Because kallsyms_lookup_name() is no longer exported in the
334 * mainline kernel we are forced to resort to somewhat drastic
335 * measures. This function replaces the functionality by performing
336 * an upcall to user space where /proc/kallsyms is consulted for
337 * the requested address.
338 */
339#define GET_KALLSYMS_ADDR_CMD \
340 "awk '{ if ( $3 == \"kallsyms_lookup_name\") { print $1 } }' " \
341 "/proc/kallsyms >/proc/sys/kernel/spl/kallsyms_lookup_name"
342
343static int
344set_kallsyms_lookup_name(void)
345{
346 char sh_path[] = "/bin/sh";
347 char *argv[] = { sh_path,
348 "-c",
349 GET_KALLSYMS_ADDR_CMD,
350 NULL };
351 char *envp[] = { "HOME=/",
352 "TERM=linux",
353 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
354 NULL };
355 int rc;
356
357 rc = call_usermodehelper(sh_path, argv, envp, 1);
358 if (rc)
96dded38
BB
359 printk("SPL: Failed user helper '%s %s %s', rc = %d\n",
360 argv[0], argv[1], argv[2], rc);
d1ff2312 361
96dded38 362 return rc;
d1ff2312
BB
363}
364#endif
365
51a727e9
BB
366static int
367__init spl_init(void)
57d1b188 368{
369 int rc = 0;
f23e92fa 370
57d1b188 371 if ((rc = debug_init()))
18c9eadf 372 return rc;
f23e92fa 373
2fb9b26a 374 if ((rc = spl_kmem_init()))
d28db80f 375 GOTO(out1, rc);
8d0f1ee9 376
9ab1ac14 377 if ((rc = spl_mutex_init()))
d28db80f 378 GOTO(out2, rc);
9ab1ac14 379
d28db80f 380 if ((rc = spl_rw_init()))
9ab1ac14 381 GOTO(out3, rc);
8d0f1ee9 382
d28db80f 383 if ((rc = spl_taskq_init()))
9ab1ac14 384 GOTO(out4, rc);
af828292 385
d28db80f 386 if ((rc = vn_init()))
04a479f7 387 GOTO(out5, rc);
388
d28db80f 389 if ((rc = proc_init()))
e9cb2b4f
BB
390 GOTO(out6, rc);
391
d28db80f
BB
392 if ((rc = kstat_init()))
393 GOTO(out7, rc);
394
57d1b188 395 if ((rc = set_hostid()))
d28db80f 396 GOTO(out8, rc = -EADDRNOTAVAIL);
f23e92fa 397
96dded38 398#ifndef HAVE_KALLSYMS_LOOKUP_NAME
d1ff2312 399 if ((rc = set_kallsyms_lookup_name()))
d28db80f 400 GOTO(out8, rc = -EADDRNOTAVAIL);
96dded38
BB
401#endif /* HAVE_KALLSYMS_LOOKUP_NAME */
402
403 if ((rc = spl_kmem_init_kallsyms_lookup()))
d28db80f 404 GOTO(out8, rc);
d1ff2312 405
0cbaeb11 406 printk("SPL: Loaded Solaris Porting Layer v%s\n", SPL_META_VERSION);
57d1b188 407 RETURN(rc);
d28db80f 408out8:
04a479f7 409 kstat_fini();
d28db80f 410out7:
57d1b188 411 proc_fini();
d28db80f 412out6:
57d1b188 413 vn_fini();
d28db80f 414out5:
e9cb2b4f 415 spl_taskq_fini();
d28db80f
BB
416out4:
417 spl_rw_fini();
9ab1ac14 418out3:
419 spl_mutex_fini();
8d0f1ee9 420out2:
2fb9b26a 421 spl_kmem_fini();
d28db80f 422out1:
57d1b188 423 debug_fini();
8d0f1ee9 424
57d1b188 425 printk("SPL: Failed to Load Solaris Porting Layer v%s, "
0cbaeb11 426 "rc = %d\n", SPL_META_VERSION, rc);
18c9eadf 427 return rc;
70eadc19 428}
429
51a727e9
BB
430static void
431spl_fini(void)
70eadc19 432{
57d1b188 433 ENTRY;
434
0cbaeb11 435 printk("SPL: Unloaded Solaris Porting Layer v%s\n", SPL_META_VERSION);
04a479f7 436 kstat_fini();
57d1b188 437 proc_fini();
af828292 438 vn_fini();
e9cb2b4f 439 spl_taskq_fini();
d28db80f 440 spl_rw_fini();
2fb9b26a 441 spl_mutex_fini();
442 spl_kmem_fini();
57d1b188 443 debug_fini();
70eadc19 444}
445
51a727e9
BB
446/* Called when a dependent module is loaded */
447void
448spl_setup(void)
449{
82a358d9
BB
450 int rc;
451
51a727e9
BB
452 /*
453 * At module load time the pwd is set to '/' on a Solaris system.
454 * On a Linux system will be set to whatever directory the caller
455 * was in when executing insmod/modprobe.
456 */
82a358d9
BB
457 rc = vn_set_pwd("/");
458 if (rc)
459 printk("SPL: Warning unable to set pwd to '/': %d\n", rc);
51a727e9
BB
460}
461EXPORT_SYMBOL(spl_setup);
462
463/* Called when a dependent module is unloaded */
464void
465spl_cleanup(void)
466{
467}
468EXPORT_SYMBOL(spl_cleanup);
469
70eadc19 470module_init(spl_init);
471module_exit(spl_fini);
472
473MODULE_AUTHOR("Lawrence Livermore National Labs");
474MODULE_DESCRIPTION("Solaris Porting Layer");
475MODULE_LICENSE("GPL");