2014-07-17 01:04:10 +03:00
/*
* Copyright 2014 Advanced Micro Devices , Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*
*/
# include <linux/slab.h>
# include <linux/list.h>
# include "kfd_device_queue_manager.h"
# include "kfd_priv.h"
# include "kfd_kernel_queue.h"
static inline struct process_queue_node * get_queue_by_qid (
struct process_queue_manager * pqm , unsigned int qid )
{
struct process_queue_node * pqn ;
list_for_each_entry ( pqn , & pqm - > queues , process_queue_list ) {
2017-08-15 23:00:07 -04:00
if ( ( pqn - > q & & pqn - > q - > properties . queue_id = = qid ) | |
( pqn - > kq & & pqn - > kq - > queue - > properties . queue_id = = qid ) )
2014-07-17 01:04:10 +03:00
return pqn ;
}
return NULL ;
}
static int find_available_queue_slot ( struct process_queue_manager * pqm ,
unsigned int * qid )
{
unsigned long found ;
found = find_first_zero_bit ( pqm - > queue_slot_bitmap ,
2015-01-18 13:18:01 +02:00
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS ) ;
2014-07-17 01:04:10 +03:00
2017-08-15 23:00:05 -04:00
pr_debug ( " The new slot id %lu \n " , found ) ;
2014-07-17 01:04:10 +03:00
2015-01-18 13:18:01 +02:00
if ( found > = KFD_MAX_NUM_OF_QUEUES_PER_PROCESS ) {
2017-08-15 23:00:05 -04:00
pr_info ( " Cannot open more queues for process with pasid %d \n " ,
2014-07-17 01:04:10 +03:00
pqm - > process - > pasid ) ;
return - ENOMEM ;
}
set_bit ( found , pqm - > queue_slot_bitmap ) ;
* qid = found ;
return 0 ;
}
2017-09-27 00:09:52 -04:00
void kfd_process_dequeue_from_device ( struct kfd_process_device * pdd )
{
struct kfd_dev * dev = pdd - > dev ;
if ( pdd - > already_dequeued )
return ;
dev - > dqm - > ops . process_termination ( dev - > dqm , & pdd - > qpd ) ;
pdd - > already_dequeued = true ;
}
void kfd_process_dequeue_from_all_devices ( struct kfd_process * p )
{
struct kfd_process_device * pdd ;
list_for_each_entry ( pdd , & p - > per_device_data , per_device_list )
kfd_process_dequeue_from_device ( pdd ) ;
}
2014-07-17 01:04:10 +03:00
int pqm_init ( struct process_queue_manager * pqm , struct kfd_process * p )
{
INIT_LIST_HEAD ( & pqm - > queues ) ;
pqm - > queue_slot_bitmap =
2015-01-18 13:18:01 +02:00
kzalloc ( DIV_ROUND_UP ( KFD_MAX_NUM_OF_QUEUES_PER_PROCESS ,
2014-07-17 01:04:10 +03:00
BITS_PER_BYTE ) , GFP_KERNEL ) ;
2017-08-15 23:00:06 -04:00
if ( ! pqm - > queue_slot_bitmap )
2014-07-17 01:04:10 +03:00
return - ENOMEM ;
pqm - > process = p ;
return 0 ;
}
void pqm_uninit ( struct process_queue_manager * pqm )
{
struct process_queue_node * pqn , * next ;
list_for_each_entry_safe ( pqn , next , & pqm - > queues , process_queue_list ) {
2017-09-27 00:09:52 -04:00
uninit_queue ( pqn - > q ) ;
list_del ( & pqn - > process_queue_list ) ;
kfree ( pqn ) ;
2014-07-17 01:04:10 +03:00
}
2017-09-27 00:09:52 -04:00
2014-07-17 01:04:10 +03:00
kfree ( pqm - > queue_slot_bitmap ) ;
pqm - > queue_slot_bitmap = NULL ;
}
static int create_cp_queue ( struct process_queue_manager * pqm ,
struct kfd_dev * dev , struct queue * * q ,
struct queue_properties * q_properties ,
struct file * f , unsigned int qid )
{
int retval ;
/* Doorbell initialized in user space*/
q_properties - > doorbell_ptr = NULL ;
/* let DQM handle it*/
q_properties - > vmid = 0 ;
q_properties - > queue_id = qid ;
2016-09-17 15:01:45 +10:00
retval = init_queue ( q , q_properties ) ;
2014-07-17 01:04:10 +03:00
if ( retval ! = 0 )
2017-08-15 23:00:07 -04:00
return retval ;
2014-07-17 01:04:10 +03:00
( * q ) - > device = dev ;
( * q ) - > process = pqm - > process ;
2017-08-15 23:00:05 -04:00
pr_debug ( " PQM After init queue " ) ;
2014-07-17 01:04:10 +03:00
return retval ;
}
int pqm_create_queue ( struct process_queue_manager * pqm ,
struct kfd_dev * dev ,
struct file * f ,
struct queue_properties * properties ,
unsigned int * qid )
{
int retval ;
struct kfd_process_device * pdd ;
struct queue * q ;
struct process_queue_node * pqn ;
struct kernel_queue * kq ;
2017-09-27 00:09:53 -04:00
enum kfd_queue_type type = properties - > type ;
2017-09-27 00:09:55 -04:00
unsigned int max_queues = 127 ; /* HWS limit */
2014-07-17 01:04:10 +03:00
q = NULL ;
kq = NULL ;
2014-11-18 14:00:04 +02:00
pdd = kfd_get_process_device_data ( dev , pqm - > process ) ;
if ( ! pdd ) {
pr_err ( " Process device data doesn't exist \n " ) ;
return - 1 ;
}
2014-07-17 01:04:10 +03:00
2015-05-20 13:43:04 +03:00
/*
* for debug process , verify that it is within the static queues limit
* currently limit is set to half of the total avail HQD slots
* If we are just about to create DIQ , the is_debug flag is not set yet
* Hence we also check the type as well
*/
2017-09-27 00:09:55 -04:00
if ( ( pdd - > qpd . is_debug ) | | ( type = = KFD_QUEUE_TYPE_DIQ ) )
max_queues = dev - > device_info - > max_no_of_hqd / 2 ;
if ( pdd - > qpd . queue_count > = max_queues )
return - ENOSPC ;
2015-05-20 13:43:04 +03:00
2014-07-17 01:04:10 +03:00
retval = find_available_queue_slot ( pqm , qid ) ;
if ( retval ! = 0 )
return retval ;
2017-11-01 19:21:30 -04:00
if ( list_empty ( & pdd - > qpd . queues_list ) & &
2017-11-14 16:41:17 -05:00
list_empty ( & pdd - > qpd . priv_queue_list ) )
2015-01-12 14:26:10 +02:00
dev - > dqm - > ops . register_process ( dev - > dqm , & pdd - > qpd ) ;
2014-07-17 01:04:10 +03:00
2017-08-15 23:00:08 -04:00
pqn = kzalloc ( sizeof ( * pqn ) , GFP_KERNEL ) ;
2014-07-17 01:04:10 +03:00
if ( ! pqn ) {
retval = - ENOMEM ;
goto err_allocate_pqn ;
}
switch ( type ) {
2015-01-03 22:12:32 +02:00
case KFD_QUEUE_TYPE_SDMA :
2017-11-01 19:21:57 -04:00
if ( dev - > dqm - > queue_count > =
CIK_SDMA_QUEUES_PER_ENGINE * CIK_SDMA_ENGINE_NUM ) {
pr_err ( " Over-subscription is not allowed for SDMA. \n " ) ;
retval = - EPERM ;
goto err_create_queue ;
}
retval = create_cp_queue ( pqm , dev , & q , properties , f , * qid ) ;
if ( retval ! = 0 )
goto err_create_queue ;
pqn - > q = q ;
pqn - > kq = NULL ;
2017-11-24 18:10:54 -05:00
retval = dev - > dqm - > ops . create_queue ( dev - > dqm , q , & pdd - > qpd ) ;
2017-11-01 19:21:57 -04:00
pr_debug ( " DQM returned %d for create_queue \n " , retval ) ;
print_queue ( q ) ;
break ;
2014-07-17 01:04:10 +03:00
case KFD_QUEUE_TYPE_COMPUTE :
/* check if there is over subscription */
2018-01-04 17:17:43 -05:00
if ( ( dev - > dqm - > sched_policy = =
KFD_SCHED_POLICY_HWS_NO_OVERSUBSCRIPTION ) & &
2017-09-20 18:10:18 -04:00
( ( dev - > dqm - > processes_count > = dev - > vm_info . vmid_num_kfd ) | |
2017-02-03 16:28:48 -05:00
( dev - > dqm - > queue_count > = get_queues_num ( dev - > dqm ) ) ) ) {
2017-08-15 23:00:05 -04:00
pr_err ( " Over-subscription is not allowed in radeon_kfd.sched_policy == 1 \n " ) ;
2014-07-17 01:04:10 +03:00
retval = - EPERM ;
goto err_create_queue ;
}
2017-09-27 00:09:53 -04:00
retval = create_cp_queue ( pqm , dev , & q , properties , f , * qid ) ;
2014-07-17 01:04:10 +03:00
if ( retval ! = 0 )
goto err_create_queue ;
pqn - > q = q ;
pqn - > kq = NULL ;
2017-11-24 18:10:54 -05:00
retval = dev - > dqm - > ops . create_queue ( dev - > dqm , q , & pdd - > qpd ) ;
2015-01-18 13:18:01 +02:00
pr_debug ( " DQM returned %d for create_queue \n " , retval ) ;
2014-07-17 01:04:10 +03:00
print_queue ( q ) ;
break ;
case KFD_QUEUE_TYPE_DIQ :
kq = kernel_queue_init ( dev , KFD_QUEUE_TYPE_DIQ ) ;
2017-08-15 23:00:06 -04:00
if ( ! kq ) {
2014-11-25 13:24:51 +03:00
retval = - ENOMEM ;
2014-07-17 01:04:10 +03:00
goto err_create_queue ;
}
kq - > queue - > properties . queue_id = * qid ;
pqn - > kq = kq ;
pqn - > q = NULL ;
2015-01-12 14:26:10 +02:00
retval = dev - > dqm - > ops . create_kernel_queue ( dev - > dqm ,
kq , & pdd - > qpd ) ;
2014-07-17 01:04:10 +03:00
break ;
default :
2017-08-15 23:00:12 -04:00
WARN ( 1 , " Invalid queue type %d " , type ) ;
retval = - EINVAL ;
2014-07-17 01:04:10 +03:00
}
if ( retval ! = 0 ) {
2017-08-15 23:00:05 -04:00
pr_err ( " DQM create queue failed \n " ) ;
2014-07-17 01:04:10 +03:00
goto err_create_queue ;
}
2018-04-10 17:33:05 -04:00
if ( q )
/* Return the doorbell offset within the doorbell page
* to the caller so it can be passed up to user mode
* ( in bytes ) .
*/
properties - > doorbell_off =
( q - > properties . doorbell_off * sizeof ( uint32_t ) ) &
( kfd_doorbell_process_slice ( dev ) - 1 ) ;
2017-08-15 23:00:05 -04:00
pr_debug ( " PQM After DQM create queue \n " ) ;
2014-07-17 01:04:10 +03:00
list_add ( & pqn - > process_queue_list , & pqm - > queues ) ;
if ( q ) {
2017-08-15 23:00:05 -04:00
pr_debug ( " PQM done creating queue \n " ) ;
2017-09-27 00:09:53 -04:00
print_queue_properties ( & q - > properties ) ;
2014-07-17 01:04:10 +03:00
}
return retval ;
err_create_queue :
kfree ( pqn ) ;
err_allocate_pqn :
2015-01-15 17:14:47 +02:00
/* check if queues list is empty unregister process from device */
2014-07-17 01:04:10 +03:00
clear_bit ( * qid , pqm - > queue_slot_bitmap ) ;
2017-11-01 19:21:30 -04:00
if ( list_empty ( & pdd - > qpd . queues_list ) & &
list_empty ( & pdd - > qpd . priv_queue_list ) )
2015-01-29 11:45:31 +10:00
dev - > dqm - > ops . unregister_process ( dev - > dqm , & pdd - > qpd ) ;
2014-07-17 01:04:10 +03:00
return retval ;
}
int pqm_destroy_queue ( struct process_queue_manager * pqm , unsigned int qid )
{
struct process_queue_node * pqn ;
struct kfd_process_device * pdd ;
struct device_queue_manager * dqm ;
struct kfd_dev * dev ;
int retval ;
dqm = NULL ;
retval = 0 ;
pqn = get_queue_by_qid ( pqm , qid ) ;
2017-08-15 23:00:06 -04:00
if ( ! pqn ) {
2017-08-15 23:00:05 -04:00
pr_err ( " Queue id does not match any known queue \n " ) ;
2014-07-17 01:04:10 +03:00
return - EINVAL ;
}
dev = NULL ;
if ( pqn - > kq )
dev = pqn - > kq - > dev ;
if ( pqn - > q )
dev = pqn - > q - > device ;
2017-08-15 23:00:12 -04:00
if ( WARN_ON ( ! dev ) )
return - ENODEV ;
2014-07-17 01:04:10 +03:00
2014-11-18 14:00:04 +02:00
pdd = kfd_get_process_device_data ( dev , pqm - > process ) ;
if ( ! pdd ) {
pr_err ( " Process device data doesn't exist \n " ) ;
return - 1 ;
}
2014-07-17 01:04:10 +03:00
if ( pqn - > kq ) {
/* destroy kernel queue (DIQ) */
dqm = pqn - > kq - > dev - > dqm ;
2015-01-12 14:26:10 +02:00
dqm - > ops . destroy_kernel_queue ( dqm , pqn - > kq , & pdd - > qpd ) ;
2014-07-17 01:04:10 +03:00
kernel_queue_uninit ( pqn - > kq ) ;
}
if ( pqn - > q ) {
dqm = pqn - > q - > device - > dqm ;
2015-01-12 14:26:10 +02:00
retval = dqm - > ops . destroy_queue ( dqm , & pdd - > qpd , pqn - > q ) ;
2017-11-27 18:29:44 -05:00
if ( retval ) {
pr_debug ( " Destroy queue failed, returned %d \n " , retval ) ;
goto err_destroy_queue ;
}
2014-07-17 01:04:10 +03:00
uninit_queue ( pqn - > q ) ;
}
list_del ( & pqn - > process_queue_list ) ;
kfree ( pqn ) ;
clear_bit ( qid , pqm - > queue_slot_bitmap ) ;
2017-11-01 19:21:30 -04:00
if ( list_empty ( & pdd - > qpd . queues_list ) & &
list_empty ( & pdd - > qpd . priv_queue_list ) )
2015-01-12 14:26:10 +02:00
dqm - > ops . unregister_process ( dqm , & pdd - > qpd ) ;
2014-07-17 01:04:10 +03:00
2017-11-27 18:29:44 -05:00
err_destroy_queue :
2014-07-17 01:04:10 +03:00
return retval ;
}
int pqm_update_queue ( struct process_queue_manager * pqm , unsigned int qid ,
struct queue_properties * p )
{
int retval ;
struct process_queue_node * pqn ;
pqn = get_queue_by_qid ( pqm , qid ) ;
2015-01-20 14:57:19 +02:00
if ( ! pqn ) {
2017-08-15 23:00:05 -04:00
pr_debug ( " No queue %d exists for update operation \n " , qid ) ;
2015-01-20 14:57:19 +02:00
return - EFAULT ;
}
2014-07-17 01:04:10 +03:00
pqn - > q - > properties . queue_address = p - > queue_address ;
pqn - > q - > properties . queue_size = p - > queue_size ;
pqn - > q - > properties . queue_percent = p - > queue_percent ;
pqn - > q - > properties . priority = p - > priority ;
2015-01-12 14:26:10 +02:00
retval = pqn - > q - > device - > dqm - > ops . update_queue ( pqn - > q - > device - > dqm ,
pqn - > q ) ;
2014-07-17 01:04:10 +03:00
if ( retval ! = 0 )
return retval ;
return 0 ;
}
2015-05-20 13:48:26 +03:00
struct kernel_queue * pqm_get_kernel_queue (
2014-11-20 17:16:23 +08:00
struct process_queue_manager * pqm ,
2014-07-17 01:04:10 +03:00
unsigned int qid )
{
struct process_queue_node * pqn ;
pqn = get_queue_by_qid ( pqm , qid ) ;
if ( pqn & & pqn - > kq )
return pqn - > kq ;
return NULL ;
}
2017-11-27 18:29:49 -05:00
# if defined(CONFIG_DEBUG_FS)
2014-07-17 01:04:10 +03:00
2017-11-27 18:29:49 -05:00
int pqm_debugfs_mqds ( struct seq_file * m , void * data )
{
struct process_queue_manager * pqm = data ;
struct process_queue_node * pqn ;
struct queue * q ;
enum KFD_MQD_TYPE mqd_type ;
struct mqd_manager * mqd_manager ;
int r = 0 ;
list_for_each_entry ( pqn , & pqm - > queues , process_queue_list ) {
if ( pqn - > q ) {
q = pqn - > q ;
switch ( q - > properties . type ) {
case KFD_QUEUE_TYPE_SDMA :
seq_printf ( m , " SDMA queue on device %x \n " ,
q - > device - > id ) ;
mqd_type = KFD_MQD_TYPE_SDMA ;
break ;
case KFD_QUEUE_TYPE_COMPUTE :
seq_printf ( m , " Compute queue on device %x \n " ,
q - > device - > id ) ;
mqd_type = KFD_MQD_TYPE_CP ;
break ;
default :
seq_printf ( m ,
" Bad user queue type %d on device %x \n " ,
q - > properties . type , q - > device - > id ) ;
continue ;
}
mqd_manager = q - > device - > dqm - > ops . get_mqd_manager (
q - > device - > dqm , mqd_type ) ;
} else if ( pqn - > kq ) {
q = pqn - > kq - > queue ;
mqd_manager = pqn - > kq - > mqd ;
switch ( q - > properties . type ) {
case KFD_QUEUE_TYPE_DIQ :
seq_printf ( m , " DIQ on device %x \n " ,
pqn - > kq - > dev - > id ) ;
mqd_type = KFD_MQD_TYPE_HIQ ;
break ;
default :
seq_printf ( m ,
" Bad kernel queue type %d on device %x \n " ,
q - > properties . type ,
pqn - > kq - > dev - > id ) ;
continue ;
}
} else {
seq_printf ( m ,
" Weird: Queue node with neither kernel nor user queue \n " ) ;
continue ;
}
r = mqd_manager - > debugfs_show_mqd ( m , q - > mqd ) ;
if ( r ! = 0 )
break ;
}
return r ;
}
# endif