GNU Linux-libre 4.9.282-gnu1
[releases.git] / drivers / staging / lustre / lnet / libcfs / libcfs_lock.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * GPL HEADER END
17  */
18 /* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
19  * Copyright (c) 2012, 2015 Intel Corporation.
20  */
21 /*
22  * This file is part of Lustre, http://www.lustre.org/
23  * Lustre is a trademark of Sun Microsystems, Inc.
24  *
25  * Author: liang@whamcloud.com
26  */
27
28 #define DEBUG_SUBSYSTEM S_LNET
29
30 #include "../../include/linux/libcfs/libcfs.h"
31
32 /** destroy cpu-partition lock, see libcfs_private.h for more detail */
33 void
34 cfs_percpt_lock_free(struct cfs_percpt_lock *pcl)
35 {
36         LASSERT(pcl->pcl_locks);
37         LASSERT(!pcl->pcl_locked);
38
39         cfs_percpt_free(pcl->pcl_locks);
40         LIBCFS_FREE(pcl, sizeof(*pcl));
41 }
42 EXPORT_SYMBOL(cfs_percpt_lock_free);
43
44 /**
45  * create cpu-partition lock, see libcfs_private.h for more detail.
46  *
47  * cpu-partition lock is designed for large-scale SMP system, so we need to
48  * reduce cacheline conflict as possible as we can, that's the
49  * reason we always allocate cacheline-aligned memory block.
50  */
51 struct cfs_percpt_lock *
52 cfs_percpt_lock_create(struct cfs_cpt_table *cptab,
53                        struct lock_class_key *keys)
54 {
55         struct cfs_percpt_lock  *pcl;
56         spinlock_t              *lock;
57         int                     i;
58
59         /* NB: cptab can be NULL, pcl will be for HW CPUs on that case */
60         LIBCFS_ALLOC(pcl, sizeof(*pcl));
61         if (!pcl)
62                 return NULL;
63
64         pcl->pcl_cptab = cptab;
65         pcl->pcl_locks = cfs_percpt_alloc(cptab, sizeof(*lock));
66         if (!pcl->pcl_locks) {
67                 LIBCFS_FREE(pcl, sizeof(*pcl));
68                 return NULL;
69         }
70
71         if (!keys)
72                 CWARN("Cannot setup class key for percpt lock, you may see recursive locking warnings which are actually fake.\n");
73
74         cfs_percpt_for_each(lock, i, pcl->pcl_locks) {
75                 spin_lock_init(lock);
76                 if (keys != NULL)
77                         lockdep_set_class(lock, &keys[i]);
78         }
79
80         return pcl;
81 }
82 EXPORT_SYMBOL(cfs_percpt_lock_create);
83
84 /**
85  * lock a CPU partition
86  *
87  * \a index != CFS_PERCPT_LOCK_EX
88  *     hold private lock indexed by \a index
89  *
90  * \a index == CFS_PERCPT_LOCK_EX
91  *     exclusively lock @pcl and nobody can take private lock
92  */
93 void
94 cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index)
95         __acquires(pcl->pcl_locks)
96 {
97         int     ncpt = cfs_cpt_number(pcl->pcl_cptab);
98         int     i;
99
100         LASSERT(index >= CFS_PERCPT_LOCK_EX && index < ncpt);
101
102         if (ncpt == 1) {
103                 index = 0;
104         } else { /* serialize with exclusive lock */
105                 while (pcl->pcl_locked)
106                         cpu_relax();
107         }
108
109         if (likely(index != CFS_PERCPT_LOCK_EX)) {
110                 spin_lock(pcl->pcl_locks[index]);
111                 return;
112         }
113
114         /* exclusive lock request */
115         for (i = 0; i < ncpt; i++) {
116                 spin_lock(pcl->pcl_locks[i]);
117                 if (i == 0) {
118                         LASSERT(!pcl->pcl_locked);
119                         /* nobody should take private lock after this
120                          * so I wouldn't starve for too long time
121                          */
122                         pcl->pcl_locked = 1;
123                 }
124         }
125 }
126 EXPORT_SYMBOL(cfs_percpt_lock);
127
128 /** unlock a CPU partition */
129 void
130 cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index)
131         __releases(pcl->pcl_locks)
132 {
133         int     ncpt = cfs_cpt_number(pcl->pcl_cptab);
134         int     i;
135
136         index = ncpt == 1 ? 0 : index;
137
138         if (likely(index != CFS_PERCPT_LOCK_EX)) {
139                 spin_unlock(pcl->pcl_locks[index]);
140                 return;
141         }
142
143         for (i = ncpt - 1; i >= 0; i--) {
144                 if (i == 0) {
145                         LASSERT(pcl->pcl_locked);
146                         pcl->pcl_locked = 0;
147                 }
148                 spin_unlock(pcl->pcl_locks[i]);
149         }
150 }
151 EXPORT_SYMBOL(cfs_percpt_unlock);