134c0dad7SMichael Holzheu /* 234c0dad7SMichael Holzheu * Hypervisor filesystem for Linux on s390 334c0dad7SMichael Holzheu * 434c0dad7SMichael Holzheu * Diag 0C implementation 534c0dad7SMichael Holzheu * 634c0dad7SMichael Holzheu * Copyright IBM Corp. 2014 734c0dad7SMichael Holzheu */ 834c0dad7SMichael Holzheu 934c0dad7SMichael Holzheu #include <linux/slab.h> 1034c0dad7SMichael Holzheu #include <linux/cpu.h> 11*1ec2772eSMartin Schwidefsky #include <asm/diag.h> 1234c0dad7SMichael Holzheu #include <asm/hypfs.h> 1334c0dad7SMichael Holzheu #include "hypfs.h" 1434c0dad7SMichael Holzheu 1534c0dad7SMichael Holzheu #define DBFS_D0C_HDR_VERSION 0 1634c0dad7SMichael Holzheu 1734c0dad7SMichael Holzheu /* 1834c0dad7SMichael Holzheu * Execute diagnose 0c in 31 bit mode 1934c0dad7SMichael Holzheu */ 2034c0dad7SMichael Holzheu static void diag0c(struct hypfs_diag0c_entry *entry) 2134c0dad7SMichael Holzheu { 22*1ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X00C); 2334c0dad7SMichael Holzheu asm volatile ( 2434c0dad7SMichael Holzheu " sam31\n" 2534c0dad7SMichael Holzheu " diag %0,%0,0x0c\n" 2634c0dad7SMichael Holzheu " sam64\n" 2734c0dad7SMichael Holzheu : /* no output register */ 2834c0dad7SMichael Holzheu : "a" (entry) 2934c0dad7SMichael Holzheu : "memory"); 3034c0dad7SMichael Holzheu } 3134c0dad7SMichael Holzheu 3234c0dad7SMichael Holzheu /* 3334c0dad7SMichael Holzheu * Get hypfs_diag0c_entry from CPU vector and store diag0c data 3434c0dad7SMichael Holzheu */ 3534c0dad7SMichael Holzheu static void diag0c_fn(void *data) 3634c0dad7SMichael Holzheu { 3734c0dad7SMichael Holzheu diag0c(((void **) data)[smp_processor_id()]); 3834c0dad7SMichael Holzheu } 3934c0dad7SMichael Holzheu 4034c0dad7SMichael Holzheu /* 4134c0dad7SMichael Holzheu * Allocate buffer and store diag 0c data 4234c0dad7SMichael Holzheu */ 4334c0dad7SMichael Holzheu static void *diag0c_store(unsigned int *count) 4434c0dad7SMichael Holzheu { 4534c0dad7SMichael Holzheu struct hypfs_diag0c_data *diag0c_data; 4634c0dad7SMichael Holzheu unsigned int cpu_count, cpu, i; 4734c0dad7SMichael Holzheu void **cpu_vec; 4834c0dad7SMichael Holzheu 4934c0dad7SMichael Holzheu get_online_cpus(); 5034c0dad7SMichael Holzheu cpu_count = num_online_cpus(); 5134c0dad7SMichael Holzheu cpu_vec = kmalloc(sizeof(*cpu_vec) * num_possible_cpus(), GFP_KERNEL); 5234c0dad7SMichael Holzheu if (!cpu_vec) 5334c0dad7SMichael Holzheu goto fail_put_online_cpus; 5434c0dad7SMichael Holzheu /* Note: Diag 0c needs 8 byte alignment and real storage */ 5534c0dad7SMichael Holzheu diag0c_data = kzalloc(sizeof(struct hypfs_diag0c_hdr) + 5634c0dad7SMichael Holzheu cpu_count * sizeof(struct hypfs_diag0c_entry), 5734c0dad7SMichael Holzheu GFP_KERNEL | GFP_DMA); 5834c0dad7SMichael Holzheu if (!diag0c_data) 5934c0dad7SMichael Holzheu goto fail_kfree_cpu_vec; 6034c0dad7SMichael Holzheu i = 0; 6134c0dad7SMichael Holzheu /* Fill CPU vector for each online CPU */ 6234c0dad7SMichael Holzheu for_each_online_cpu(cpu) { 6334c0dad7SMichael Holzheu diag0c_data->entry[i].cpu = cpu; 6434c0dad7SMichael Holzheu cpu_vec[cpu] = &diag0c_data->entry[i++]; 6534c0dad7SMichael Holzheu } 6634c0dad7SMichael Holzheu /* Collect data all CPUs */ 6734c0dad7SMichael Holzheu on_each_cpu(diag0c_fn, cpu_vec, 1); 6834c0dad7SMichael Holzheu *count = cpu_count; 6934c0dad7SMichael Holzheu kfree(cpu_vec); 7034c0dad7SMichael Holzheu put_online_cpus(); 7134c0dad7SMichael Holzheu return diag0c_data; 7234c0dad7SMichael Holzheu 7334c0dad7SMichael Holzheu fail_kfree_cpu_vec: 7434c0dad7SMichael Holzheu kfree(cpu_vec); 7534c0dad7SMichael Holzheu fail_put_online_cpus: 7634c0dad7SMichael Holzheu put_online_cpus(); 7734c0dad7SMichael Holzheu return ERR_PTR(-ENOMEM); 7834c0dad7SMichael Holzheu } 7934c0dad7SMichael Holzheu 8034c0dad7SMichael Holzheu /* 8134c0dad7SMichael Holzheu * Hypfs DBFS callback: Free diag 0c data 8234c0dad7SMichael Holzheu */ 8334c0dad7SMichael Holzheu static void dbfs_diag0c_free(const void *data) 8434c0dad7SMichael Holzheu { 8534c0dad7SMichael Holzheu kfree(data); 8634c0dad7SMichael Holzheu } 8734c0dad7SMichael Holzheu 8834c0dad7SMichael Holzheu /* 8934c0dad7SMichael Holzheu * Hypfs DBFS callback: Create diag 0c data 9034c0dad7SMichael Holzheu */ 9134c0dad7SMichael Holzheu static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size) 9234c0dad7SMichael Holzheu { 9334c0dad7SMichael Holzheu struct hypfs_diag0c_data *diag0c_data; 9434c0dad7SMichael Holzheu unsigned int count; 9534c0dad7SMichael Holzheu 9634c0dad7SMichael Holzheu diag0c_data = diag0c_store(&count); 9734c0dad7SMichael Holzheu if (IS_ERR(diag0c_data)) 9834c0dad7SMichael Holzheu return PTR_ERR(diag0c_data); 9934c0dad7SMichael Holzheu memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr)); 10034c0dad7SMichael Holzheu get_tod_clock_ext(diag0c_data->hdr.tod_ext); 10134c0dad7SMichael Holzheu diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry); 10234c0dad7SMichael Holzheu diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION; 10334c0dad7SMichael Holzheu diag0c_data->hdr.count = count; 10434c0dad7SMichael Holzheu *data = diag0c_data; 10534c0dad7SMichael Holzheu *data_free_ptr = diag0c_data; 10634c0dad7SMichael Holzheu *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr); 10734c0dad7SMichael Holzheu return 0; 10834c0dad7SMichael Holzheu } 10934c0dad7SMichael Holzheu 11034c0dad7SMichael Holzheu /* 11134c0dad7SMichael Holzheu * Hypfs DBFS file structure 11234c0dad7SMichael Holzheu */ 11334c0dad7SMichael Holzheu static struct hypfs_dbfs_file dbfs_file_0c = { 11434c0dad7SMichael Holzheu .name = "diag_0c", 11534c0dad7SMichael Holzheu .data_create = dbfs_diag0c_create, 11634c0dad7SMichael Holzheu .data_free = dbfs_diag0c_free, 11734c0dad7SMichael Holzheu }; 11834c0dad7SMichael Holzheu 11934c0dad7SMichael Holzheu /* 12034c0dad7SMichael Holzheu * Initialize diag 0c interface for z/VM 12134c0dad7SMichael Holzheu */ 12234c0dad7SMichael Holzheu int __init hypfs_diag0c_init(void) 12334c0dad7SMichael Holzheu { 12434c0dad7SMichael Holzheu if (!MACHINE_IS_VM) 12534c0dad7SMichael Holzheu return 0; 12634c0dad7SMichael Holzheu return hypfs_dbfs_create_file(&dbfs_file_0c); 12734c0dad7SMichael Holzheu } 12834c0dad7SMichael Holzheu 12934c0dad7SMichael Holzheu /* 13034c0dad7SMichael Holzheu * Shutdown diag 0c interface for z/VM 13134c0dad7SMichael Holzheu */ 13234c0dad7SMichael Holzheu void hypfs_diag0c_exit(void) 13334c0dad7SMichael Holzheu { 13434c0dad7SMichael Holzheu if (!MACHINE_IS_VM) 13534c0dad7SMichael Holzheu return; 13634c0dad7SMichael Holzheu hypfs_dbfs_remove_file(&dbfs_file_0c); 13734c0dad7SMichael Holzheu } 138