1 /* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #ifndef RESOURCEGROUPS_RESOURCE_GROUP_MGR_H_
24 #define RESOURCEGROUPS_RESOURCE_GROUP_MGR_H_
25 
26 #include <stdint.h>
27 #include <memory>
28 #include <string>
29 #include <vector>
30 
31 #include "m_string.h"
32 #include "my_dbug.h"
33 #include "my_inttypes.h"
34 #include "mysql/components/service.h"
35 #include "mysql/components/services/log_builtins.h"
36 #include "mysql/components/services/log_shared.h"
37 #include "mysql/components/services/mysql_rwlock_bits.h"
38 #include "mysql/components/services/pfs_notification.h"
39 #include "mysql/components/services/pfs_resource_group.h"
40 #include "mysql/components/services/psi_thread_bits.h"
41 #include "mysql/components/services/registry.h"
42 #include "mysql/psi/mysql_mutex.h"
43 #include "sql/debug_sync.h"
44 #include "sql/log.h"
45 #include "sql/mdl.h"
46 #include "sql/resourcegroups/resource_group_basic_types.h"
47 #include "sql/sql_class.h"
48 
49 namespace dd {
50 class Resource_group;
51 }  // namespace dd
52 namespace resourcegroups {
53 class Resource_group;
54 }  // namespace resourcegroups
55 template <class Key, class Value>
56 class collation_unordered_map;
57 
58 namespace resourcegroups {
59 
60 /**
61   This is a singleton class that provides various functionalities related to
62   Resource group management, more importantly the managing and the mapping of
63   resource group names to the corresponding in-memory resource group object.
64 */
65 
66 class Resource_group_mgr {
67  public:
68   /**
69     Singleton method to return an instance of this class.
70   */
71 
72   static Resource_group_mgr *instance();
73 
74   /**
75     Destroy the singleton instance.
76   */
77 
78   static void destroy_instance();
79 
80   /**
81     Check if support for Resource group exists at runtime.
82 
83     @return true if resource group feature is supported else false.
84   */
85 
resource_group_support()86   bool resource_group_support() { return m_resource_group_support; }
87 
88   /**
89     Reason for resource group not being supported.
90 
91     @return pointer to string which indicate reason for resource group unsupport
92   */
93 
unsupport_reason()94   const char *unsupport_reason() { return m_unsupport_reason.c_str(); }
95 
96   /**
97     Set reason for resource group not being supported.
98 
99     @param  reason string representing reason for resource group unsupport.
100   */
101 
set_unsupport_reason(const std::string & reason)102   void set_unsupport_reason(const std::string &reason) {
103     m_unsupport_reason = reason;
104   }
105 
106   /**
107     Initialize the Resource group manager.
108     Must be called before instance() can be used.
109 
110     @return true if initialization failed, false otherwise.
111   */
112 
113   bool init();
114 
115   /**
116     Post initialization sequence during mysqld startup.
117 
118     @return true if post initialization failed, else false.
119   */
120 
121   bool post_init();
122 
123   /**
124     Disable and deinitialize the resource group if it was initialized before.
125   */
126 
disable_resource_group()127   void disable_resource_group() {
128     if (m_resource_group_support) {
129       LogErr(INFORMATION_LEVEL, ER_RES_GRP_FEATURE_NOT_AVAILABLE);
130       deinit();
131       m_resource_group_support = false;
132     }
133   }
134 
135   /**
136     Get the in-memory Resource group object corresponding
137     to a resource group name.
138 
139     @param  resource_group_name Name of the resource group.
140     @return Pointer to the Resource group if it exists else nullptr.
141   */
142 
143   Resource_group *get_resource_group(const std::string &resource_group_name);
144 
145   /**
146     Get the thread attributes identified by PSI thread ID.
147 
148     @param [out] pfs_thread_attr Pointer to thread attribute object which
149                                  shall be populated if the call is successful.
150     @param thread_id PFS thread identifier representing a thread of mysqld.
151 
152     @return true if call failed else false.
153   */
154 
get_thread_attributes(PSI_thread_attrs * pfs_thread_attr,ulonglong thread_id)155   bool get_thread_attributes(PSI_thread_attrs *pfs_thread_attr,
156                              ulonglong thread_id) {
157     DBUG_ASSERT(m_resource_group_support);
158     return m_resource_group_svc->get_thread_system_attrs_by_id(
159                nullptr, thread_id, pfs_thread_attr) != 0;
160   }
161 
162   /**
163     Add the resource group to the Resource group map.
164 
165     @param  resource_group_ptr  pointer to in-memory Resource_group.
166     @return true if the call failed else false.
167   */
168 
169   bool add_resource_group(std::unique_ptr<Resource_group> resource_group_ptr);
170 
171   /**
172     Remove the resource group from the map identified by it's name.
173 
174     @param name of the resource group.
175   */
176 
177   void remove_resource_group(const std::string &name);
178 
179   /**
180     Create an in-memory resource group identified by its attributes
181     and add it to the resource group map.
182 
183     @param name     Name of resource group.
184     @param type     Type of resource group.
185     @param enabled  Is resource group enabled?
186     @param cpus     Pointer to list of Range objects representing CPU IDS.
187     @param thr_priority Thread priority.
188 
189     @returns Pointer to Resource Group object if call is successful else null.
190   */
191 
192   Resource_group *create_and_add_in_resource_group_hash(
193       const LEX_CSTRING &name, Type type, bool enabled,
194       std::unique_ptr<std::vector<Range>> cpus, int thr_priority);
195 
196   /**
197     Move the Resource group of the current thread identified by from_res_group
198     to the Resource group to_res_grp.
199 
200     @param from_res_grp  Pointer to current Resource_group of the thread.
201     @param to_res_grp    Pointer to the destination Resource group which the
202                           thread will be switched to.
203     @return true if move_resource_group failed else false.
204   */
205 
206   bool move_resource_group(Resource_group *from_res_grp,
207                            Resource_group *to_res_grp);
208 
209   /**
210     Deserialize a DD resource group object into an
211     in-memory Resource group object.
212 
213     @param res_grp  Pointer to DD Resource_group object.
214     @return Pointer to in-memory Resource_group subject if
215              Successful else null.
216   */
217 
218   Resource_group *deserialize_resource_group(const dd::Resource_group *res_grp);
219 
220   /**
221     Set Resource group name in the PFS table performance_schema.threads for
222     the PFS thread id.
223 
224     @param name Pointer to name of resource group.
225     @param length length of the resource group name.
226     @param thread_id PFS thread id of the thread,
227   */
228 
set_res_grp_in_pfs(const char * name,int length,ulonglong thread_id)229   void set_res_grp_in_pfs(const char *name, int length, ulonglong thread_id) {
230     DBUG_ASSERT(m_resource_group_support);
231     m_resource_group_svc->set_thread_resource_group_by_id(
232         nullptr, thread_id, name, length, nullptr);
233   }
234 
235   /**
236     Return the SYS_default resource group instance.
237 
238     @return pointer to the SYS_default resource group.
239   */
240 
sys_default_resource_group()241   Resource_group *sys_default_resource_group() {
242     return m_sys_default_resource_group;
243   }
244 
245   /**
246     Return the USR_default resource group instance.
247 
248     @return pointer to the USR_default resource group.
249   */
250 
usr_default_resource_group()251   Resource_group *usr_default_resource_group() {
252     return m_usr_default_resource_group;
253   }
254 
255   /**
256     Check if a given Resource group is either SYS_default or USR_default.
257 
258     @return true if resource is USR_default or SYS_default else false.
259   */
260 
is_resource_group_default(const Resource_group * res_grp)261   bool is_resource_group_default(const Resource_group *res_grp) {
262     return (res_grp == m_usr_default_resource_group ||
263             res_grp == m_sys_default_resource_group);
264   }
265 
266   /**
267     Check if a thread priority setting can be done.
268 
269     @return true if thread priority setting could be done else false.
270   */
271 
thread_priority_available()272   bool thread_priority_available() {
273     DBUG_ASSERT(m_resource_group_support);
274     return m_thread_priority_available;
275   }
276 
277   /**
278     Acquire an shared MDL lock on resource group name.
279 
280     @param        thd                Pointer to THD context.
281     @param        res_grp_name       Resource group name.
282     @param        lock_duration      Duration of lock.
283     @param[out]   ticket             reference to ticket object.
284     @param        acquire_lock       true if one needs to wait on lock
285                                      else false.
286 
287     @return true if lock acquisition failed else false.
288   */
289 
290   bool acquire_shared_mdl_for_resource_group(THD *thd, const char *res_grp_name,
291                                              enum_mdl_duration lock_duration,
292                                              MDL_ticket **ticket,
293                                              bool acquire_lock);
294 
295   /**
296     Release the shared MDL lock held on a resource group.
297 
298     @param thd        THD context.
299     @param ticket     Pointer to lock ticket object.
300   */
301 
release_shared_mdl_for_resource_group(THD * thd,MDL_ticket * ticket)302   void release_shared_mdl_for_resource_group(THD *thd, MDL_ticket *ticket) {
303     DBUG_ASSERT(ticket != nullptr);
304     thd->mdl_context.release_lock(ticket);
305   }
306 
307   /**
308     String corressponding to the type of resource group.
309 
310     @param type Type of resource group.
311 
312     @return string corressponding to resource group type.
313   */
314 
resource_group_type_str(const Type & type)315   const char *resource_group_type_str(const Type &type) {
316     return type == Type::USER_RESOURCE_GROUP ? "User" : "System";
317   }
318 
319   /**
320     Number of VCPUs present in the system.
321 
322     @returns Number of VCPUs in the system.
323   */
324 
num_vcpus()325   uint32_t num_vcpus() { return m_num_vcpus; }
326 
327   void deinit();
328 
329 #ifndef DBUG_OFF  // The belows methods are required in debug build for testing.
330   bool disable_pfs_notification();
331 #endif
332 
333   /**
334     Switch Resource Group if it is requested by environment.
335 
336     @param      thd            THD context.
337     @param[out] src_res_grp    Original resource group from that
338     switching is performed
339     @param[out] dest_res_grp   Destination resource group to that
340     switching is performed
341     @param[out] ticket         Pointer to MDL ticket object.
342     @param[out] cur_ticket     Pointer to current resource group MDL ticket
343                                object.
344 
345     @return true if switching was performed, else false
346     */
347 
348   bool switch_resource_group_if_needed(
349       THD *thd, resourcegroups::Resource_group **src_res_grp,
350       resourcegroups::Resource_group **dest_res_grp, MDL_ticket **ticket,
351       MDL_ticket **cur_ticket);
352 
353   /**
354   Restore original resource group if
355   resource group switching was performed before.
356 
357   @param      thd            Thread context.
358   @param[out] src_res_grp    resource group to that
359                              switching was performed
360   @param[out] dest_res_grp   original resource group to that
361                              switching is performed
362 */
363 
restore_original_resource_group(THD * thd,resourcegroups::Resource_group * src_res_grp,resourcegroups::Resource_group * dest_res_grp)364   void restore_original_resource_group(
365       THD *thd, resourcegroups::Resource_group *src_res_grp,
366       resourcegroups::Resource_group *dest_res_grp) {
367     mysql_mutex_lock(&thd->LOCK_thd_data);
368     if (thd->resource_group_ctx()->m_cur_resource_group == nullptr ||
369         thd->resource_group_ctx()->m_cur_resource_group == src_res_grp) {
370       resourcegroups::Resource_group_mgr::instance()->move_resource_group(
371           dest_res_grp, thd->resource_group_ctx()->m_cur_resource_group);
372     }
373     mysql_mutex_unlock(&thd->LOCK_thd_data);
374     DBUG_EXECUTE_IF("pause_after_rg_switch", {
375       const char act[] =
376           "now "
377           "SIGNAL restore_finished";
378       DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(act)));
379     };);
380   }
381 
382  private:
383   /**
384     Pointer to singleton instance of the Resource_group_mgr class.
385   */
386   static Resource_group_mgr *m_instance;
387 
388   /**
389     Handles to the PFS registry, Resource Group and
390     Notification services.
391   */
392 
393   SERVICE_TYPE(registry) * m_registry_svc;
394   SERVICE_TYPE(pfs_resource_group_v3) * m_resource_group_svc;
395   SERVICE_TYPE(pfs_notification_v3) * m_notify_svc;
396   my_h_service m_h_res_grp_svc;
397   my_h_service m_h_notification_svc;
398   int m_notify_handle;
399 
400   /**
401     Pointer to USR_default and SYS_default resource groups.
402     Owernship of these pointers is resource group hash.
403   */
404   Resource_group *m_usr_default_resource_group;
405   Resource_group *m_sys_default_resource_group;
406 
407   /**
408     Map mapping resource group name with it's corresponding in-memory
409     Resource_group object
410   */
411   collation_unordered_map<std::string, std::unique_ptr<Resource_group>>
412       *m_resource_group_hash;
413 
414   /**
415     Lock protecting the resource group map.
416   */
417   mysql_rwlock_t m_map_rwlock;
418 
419   /**
420     Boolean value indicating whether setting of thread priority is allowed.
421   */
422   bool m_thread_priority_available;
423 
424   /**
425     Bool value indicating support for resource group.
426   */
427   bool m_resource_group_support;
428 
429   /**
430     String indicating reason for resource group not being supported.
431   */
432   std::string m_unsupport_reason;
433 
434   /**
435     Value indicating the number of vcpus in the system. This is initialized
436     during startup so that we do not call the platform API everytime
437     which is expensive.
438   */
439   uint32_t m_num_vcpus;
440 
Resource_group_mgr()441   Resource_group_mgr()
442       : m_registry_svc(nullptr),
443         m_resource_group_svc(nullptr),
444         m_notify_svc(nullptr),
445         m_h_res_grp_svc(nullptr),
446         m_h_notification_svc(nullptr),
447         m_notify_handle(0),
448         m_usr_default_resource_group(nullptr),
449         m_sys_default_resource_group(nullptr),
450         m_resource_group_hash(nullptr),
451         m_thread_priority_available(false),
452         m_resource_group_support(false),
453         m_num_vcpus(0) {}
454 
~Resource_group_mgr()455   ~Resource_group_mgr() {}
456 
457   // Disable copy construction and assignment for Resource_group_mgr class.
458   Resource_group_mgr(const Resource_group_mgr &) = delete;
459   void operator=(const Resource_group_mgr &) = delete;
460 };
461 }  // namespace resourcegroups
462 #endif  // RESOURCEGROUPS_RESOURCE_GROUP_MGR_H_
463