@@ -188,6 +188,173 @@ static int pseries_remove_mem_node(struct device_node *np)
188188 pseries_remove_memblock (base , lmb_size );
189189 return 0 ;
190190}
191+
192+ static bool lmb_is_removable (struct of_drconf_cell * lmb )
193+ {
194+ int i , scns_per_block ;
195+ int rc = 1 ;
196+ unsigned long pfn , block_sz ;
197+ u64 phys_addr ;
198+
199+ if (!(lmb -> flags & DRCONF_MEM_ASSIGNED ))
200+ return false;
201+
202+ block_sz = memory_block_size_bytes ();
203+ scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE ;
204+ phys_addr = lmb -> base_addr ;
205+
206+ for (i = 0 ; i < scns_per_block ; i ++ ) {
207+ pfn = PFN_DOWN (phys_addr );
208+ if (!pfn_present (pfn ))
209+ continue ;
210+
211+ rc &= is_mem_section_removable (pfn , PAGES_PER_SECTION );
212+ phys_addr += MIN_MEMORY_BLOCK_SIZE ;
213+ }
214+
215+ return rc ? true : false;
216+ }
217+
218+ static int dlpar_add_lmb (struct of_drconf_cell * );
219+
220+ static int dlpar_remove_lmb (struct of_drconf_cell * lmb )
221+ {
222+ struct memory_block * mem_block ;
223+ unsigned long block_sz ;
224+ int nid , rc ;
225+
226+ if (!lmb_is_removable (lmb ))
227+ return - EINVAL ;
228+
229+ mem_block = lmb_to_memblock (lmb );
230+ if (!mem_block )
231+ return - EINVAL ;
232+
233+ rc = device_offline (& mem_block -> dev );
234+ put_device (& mem_block -> dev );
235+ if (rc )
236+ return rc ;
237+
238+ block_sz = pseries_memory_block_size ();
239+ nid = memory_add_physaddr_to_nid (lmb -> base_addr );
240+
241+ remove_memory (nid , lmb -> base_addr , block_sz );
242+
243+ /* Update memory regions for memory remove */
244+ memblock_remove (lmb -> base_addr , block_sz );
245+
246+ dlpar_release_drc (lmb -> drc_index );
247+
248+ lmb -> flags &= ~DRCONF_MEM_ASSIGNED ;
249+ return 0 ;
250+ }
251+
252+ static int dlpar_memory_remove_by_count (u32 lmbs_to_remove ,
253+ struct property * prop )
254+ {
255+ struct of_drconf_cell * lmbs ;
256+ int lmbs_removed = 0 ;
257+ int lmbs_available = 0 ;
258+ u32 num_lmbs , * p ;
259+ int i , rc ;
260+
261+ pr_info ("Attempting to hot-remove %d LMB(s)\n" , lmbs_to_remove );
262+
263+ if (lmbs_to_remove == 0 )
264+ return - EINVAL ;
265+
266+ p = prop -> value ;
267+ num_lmbs = * p ++ ;
268+ lmbs = (struct of_drconf_cell * )p ;
269+
270+ /* Validate that there are enough LMBs to satisfy the request */
271+ for (i = 0 ; i < num_lmbs ; i ++ ) {
272+ if (lmbs [i ].flags & DRCONF_MEM_ASSIGNED )
273+ lmbs_available ++ ;
274+ }
275+
276+ if (lmbs_available < lmbs_to_remove )
277+ return - EINVAL ;
278+
279+ for (i = 0 ; i < num_lmbs && lmbs_removed < lmbs_to_remove ; i ++ ) {
280+ rc = dlpar_remove_lmb (& lmbs [i ]);
281+ if (rc )
282+ continue ;
283+
284+ lmbs_removed ++ ;
285+
286+ /* Mark this lmb so we can add it later if all of the
287+ * requested LMBs cannot be removed.
288+ */
289+ lmbs [i ].reserved = 1 ;
290+ }
291+
292+ if (lmbs_removed != lmbs_to_remove ) {
293+ pr_err ("Memory hot-remove failed, adding LMB's back\n" );
294+
295+ for (i = 0 ; i < num_lmbs ; i ++ ) {
296+ if (!lmbs [i ].reserved )
297+ continue ;
298+
299+ rc = dlpar_add_lmb (& lmbs [i ]);
300+ if (rc )
301+ pr_err ("Failed to add LMB back, drc index %x\n" ,
302+ lmbs [i ].drc_index );
303+
304+ lmbs [i ].reserved = 0 ;
305+ }
306+
307+ rc = - EINVAL ;
308+ } else {
309+ for (i = 0 ; i < num_lmbs ; i ++ ) {
310+ if (!lmbs [i ].reserved )
311+ continue ;
312+
313+ pr_info ("Memory at %llx was hot-removed\n" ,
314+ lmbs [i ].base_addr );
315+
316+ lmbs [i ].reserved = 0 ;
317+ }
318+ rc = 0 ;
319+ }
320+
321+ return rc ;
322+ }
323+
324+ static int dlpar_memory_remove_by_index (u32 drc_index , struct property * prop )
325+ {
326+ struct of_drconf_cell * lmbs ;
327+ u32 num_lmbs , * p ;
328+ int lmb_found ;
329+ int i , rc ;
330+
331+ pr_info ("Attempting to hot-remove LMB, drc index %x\n" , drc_index );
332+
333+ p = prop -> value ;
334+ num_lmbs = * p ++ ;
335+ lmbs = (struct of_drconf_cell * )p ;
336+
337+ lmb_found = 0 ;
338+ for (i = 0 ; i < num_lmbs ; i ++ ) {
339+ if (lmbs [i ].drc_index == drc_index ) {
340+ lmb_found = 1 ;
341+ rc = dlpar_remove_lmb (& lmbs [i ]);
342+ break ;
343+ }
344+ }
345+
346+ if (!lmb_found )
347+ rc = - EINVAL ;
348+
349+ if (rc )
350+ pr_info ("Failed to hot-remove memory at %llx\n" ,
351+ lmbs [i ].base_addr );
352+ else
353+ pr_info ("Memory at %llx was hot-removed\n" , lmbs [i ].base_addr );
354+
355+ return rc ;
356+ }
357+
191358#else
192359static inline int pseries_remove_memblock (unsigned long base ,
193360 unsigned int memblock_size )
@@ -198,6 +365,11 @@ static inline int pseries_remove_mem_node(struct device_node *np)
198365{
199366 return 0 ;
200367}
368+ static inline int dlpar_memory_remove (struct pseries_hp_errorlog * hp_elog )
369+ {
370+ return - EOPNOTSUPP ;
371+ }
372+
201373#endif /* CONFIG_MEMORY_HOTREMOVE */
202374
203375static int dlpar_add_lmb (struct of_drconf_cell * lmb )
@@ -292,7 +464,17 @@ static int dlpar_memory_add_by_count(u32 lmbs_to_add, struct property *prop)
292464 }
293465
294466 if (lmbs_added != lmbs_to_add ) {
295- /* TODO: remove added lmbs */
467+ pr_err ("Memory hot-add failed, removing any added LMBs\n" );
468+
469+ for (i = 0 ; i < num_lmbs ; i ++ ) {
470+ if (!lmbs [i ].reserved )
471+ continue ;
472+
473+ rc = dlpar_remove_lmb (& lmbs [i ]);
474+ if (rc )
475+ pr_err ("Failed to remove LMB, drc index %x\n" ,
476+ be32_to_cpu (lmbs [i ].drc_index ));
477+ }
296478 rc = - EINVAL ;
297479 } else {
298480 for (i = 0 ; i < num_lmbs ; i ++ ) {
@@ -398,6 +580,14 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
398580 else
399581 rc = - EINVAL ;
400582 break ;
583+ case PSERIES_HP_ELOG_ACTION_REMOVE :
584+ if (hp_elog -> id_type == PSERIES_HP_ELOG_ID_DRC_COUNT )
585+ rc = dlpar_memory_remove_by_count (count , prop );
586+ else if (hp_elog -> id_type == PSERIES_HP_ELOG_ID_DRC_INDEX )
587+ rc = dlpar_memory_remove_by_index (drc_index , prop );
588+ else
589+ rc = - EINVAL ;
590+ break ;
401591 default :
402592 pr_err ("Invalid action (%d) specified\n" , hp_elog -> action );
403593 rc = - EINVAL ;
0 commit comments